Issue 1448042: Defining a class with dict brakes attributes assignment (original) (raw)
When defining a class with dict attribute, its instances can't rebind their dict attributes.
class C(object): dict = {}
obj = C() obj.a = object()
import gc gc.get_referrers(obj.a) # => [{'a': <object object at 0x811d5b0>}]
obj.dict = {} # doesn't really bind new dict
vars(obj) # => {} object.getattribute(obj, 'dict') # => {} object.getattribute(C, 'dict') # => {..., but without "a"} obj.a # => <object object at 0x811d5b0> (no exception !)
gc.get_referrers(obj.a) # => [{'a': <object object at 0x811d5b0>, 'dict': {}}]
Although neither class nor object has an attribute "a", it's still accessible. It's also not possible to rebind dict in that object, as it gets inside real object attributes dictionary.
This behaviour has been tested on Python 2.2, 2.3 and 2.4, but may as well affect earlier versions.
Logged In: YES user_id=593130
To me, this falls under the category of 'don't do that'.
http://docs.python.org/ref/id-classes.html 2.3.2 Reserved classes of identifiers * System-defined names. These names are defined by the interpreter and its implementation ...
To me, this means to use them in the manner specified or you get what you get.
http://docs.python.org/ref/types.html#l2h-120 defines the internal usage of 'dict'. There is, as far as I know, no specified usage for rebinding 'dict'.
So unless someone has a better idea that won't slow down proper usage, I would close this as 'invalid' or 'wont fix'.
Logged In: YES user_id=1326842
Maybe this shows that it is actually a feature?
class C(object): ... pass ...
'dict' is not a normal attribute, it's a descriptor (a "getset_descriptor") generated by object's type. You can get to this object if you try hard enough:
C_dict_descriptor = C.dict['dict'] type(C_dict_descriptor).name 'getset_descriptor'
This descriptor is automatically created for most of the python classes (except for those, that have slots without dict) by 'type' object.
Since 'type' is an instance of itself, it also has it:
type_dict_descriptor = type.dict['dict']
And we can see, that it is responsible for creating the C's dict attribute:
C.dict == type_dict_descriptor.get(C, type) True
As is normal for most of the special named attributes, this one is looked up in object's type, not in its dict, and it isn't a normal dict, but a dictproxy:
type(C.dict).name 'dictproxy'
Now in your case, you create a class attribute 'dict':
class D(C): ... dict = {'a': 1} ...
Which basically does something like:
name = 'E' bases = (C,) E_namespace = { ... 'dict': {'a': 1}, ... 'doc': "set to None by type if not provided", ... 'module': "globals()['name'] if missing", ... 'weakref': "another descriptor", ... } E = type(name, bases, E_namespace)
The 'dict' attribute of this class is still provided by its type (type 'type'), and is basicaly just a dictproxy of the E_namespace:
type(E.dict).name 'dictproxy' E.dict == E_namespace True
What your class definition actually did, is it has overwritten the dict descriptor that would be normaly created by type; compare:
C.dict['dict'] <attribute '__dict__' of 'C' objects> E.dict['dict'] {'a': 1}
Now watch what happens if you create an instance of E class:
e = E() e.dict {'a': 1} e.a = 2 e.dict {'a': 1}
Basically, now the 'dict' attribute is a normal attribute, that behaves just as any other attribute, while you have lost acces to the instance's inner dict:
e.dict = {} e.dict {} e.a 2
If you inherit directly from object, which doesn't have this descriptor:
object.dict['dict'] Traceback (most recent call last): File "", line 1, in ? KeyError: 'dict'
there would be no way of accesing instance's dictinary. But since we inherited from class C, we can stil acces it:
C_dict_descriptor.get(e) {'a': 2, 'dict': {}}