0

I am wondering what kind of difference exists between class(dict) and class(str) Here is my code

class MyDict3(str):
     def __init__(self):
         self.a = None 

class MyDict(dict):
     def __init__(self):
         self.a = None

These classes are what I made for clarification and then I type below

>>> mydict['a'] = 1
>>> mydict
{'a': 1}
>>> mydict3['a'] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'MyDict3' object does not support item assignment

Why does my mydict3['a'] make an error? The difference that I made is only MyDict(dict) and MyDict(str) As far as I know, the object that I specified(dict, str) is just nothing but constructer like c++,java

Please give me a clear answer on that.

tunaBritto
  • 33
  • 3
  • 3
    `MyDict3(str)` means that MyDict3 inherits from `str`, `MyDict3(dict)` inherits from `dict`. It'd be like `class MyDict3 extends String` in Java. – Aaron Christiansen Apr 06 '16 at 16:13
  • 3
    String objects don't support item assignment. – vaultah Apr 06 '16 at 16:14
  • I got it, Thank you guys. – tunaBritto Apr 06 '16 at 16:17
  • Not sure why you expected `mydict3['a']` to work in the first place. Strings don't support item assignment; you only gave instances an `a` *attribute*. `So `mydict3.a` and `mydict.a` both exist (your `mydict['a']` *item* is independent from the `a` attribute). – Martijn Pieters Apr 06 '16 at 16:17
  • You'll also have trouble with `__init__` in `str`, as strings are immutable. See [inheritance from str or int](http://stackoverflow.com/q/2673651). – Martijn Pieters Apr 06 '16 at 16:18

1 Answers1

0

Why does my mydict3['a'] make an error? The difference that I made is only MyDict(dict) and MyDict(str) As far as I know, the object that I specified(dict, str) is just nothing but constructer like c++,java

I believe that you're doing a confusion here, thinking that a class attribute and an item are the same thing, like the following javascript code:

> foo = {'a': 42};
{ a: 42 }
> foo.a
42
> foo['a']
42
> foo.a === foo['a']
true

But in python foo.a and foo['a'] are two different mechanisms. When you call foo.a you're actually accessing the a attribute of a class, which is defined through the class definition:

class Foo:
    def __init__(self):
        self.a = 42 # declaring and defining the a attribute

so then you can access it using:

 >>> foo = Foo()
 >>> print(foo.a)
 42

But to have foo['a'] working, you have to use the indexing mechanism, which is usually used for dicts or lists:

>>> foo = {'a': 42}
>>> foo['a']
42

That mechanism is being implemented by the __getitem__ method of your class, so you can overload it if you want:

class Foo:
    def __getitem__(self, val):
        if val == 'a':
            return 42
        raise KeyError('Unknown key') # when the key is unknown, you raise a key error exception

>>> foo = Foo()
>>> foo['a']
42
>>> foo['b']
KeyError: 'Unknown key'

So, the dict class is a class that implements __getitem__ (and __setitem__ and many others), in order to provide you a proper mapping mechanism called a dictionary. There keys can be any immutable objects, and values anything. For a list, it shall be only integers (which are the positions in the list).

That being said, let's answer your question:

Why does my mydict3['a'] make an error?

obviously it's because you defined mydict3 as being an implementation of a string, which has a special implementation for the __getitem__ method: it's giving you a character at the parameter position like if the list was a list of character (like in C).

So when you're trying to index mydict3 with 'a', python just tells you that what you're asking makes no sense!

So in the end, when you say:

The difference that I made is only MyDict(dict) and MyDict(str)

it's actually a very big difference! A dict and an str do not have the same interface, and thus what you want to do cannot work!


P.S.: Actually, nothing is black or white. The implementation of a class actually is a dict, and you can access all members of a class' instance through the __dict__ member of an instance:

class Foo():
    def __init__(self):
        self.a = 42

>>> foo = Foo()
>>> foo.__dict__['a']
42

but you shall never directly access the __dict__ instance directly, and use helper functions setattr and getattr:

>>> setattr(foo, 'b', 42)
>>> getattr(foo, 'b')
42
>>> getattr(foo, 'a')
42

This is some advanced python tricks, and they should be use with care. If there's really no other way to do it, then maybe you should use that.

Also, there exists a special class that transform dict items as class members, it's the namedtuple:

>>> from collections import namedtuple
>>> d = {'a': 42, 'b': 69}
>>> SpecialDict = namedtuple('SpecialDict', d.keys())
>>> foo = SpecialDict(**d)
>>> d.a
42
>>> d.b
69

HTH

zmo
  • 24,463
  • 4
  • 54
  • 90