>>> a = object()
>>> a.x = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'x'
>>> b = lambda:0
>>> b.x = 5
>>> b.x
5
Why do instances of the object class not have a __dict__, causing it to behave as semantically immutable? What were the reasons for choosing this design?
Specifically, why:
instances of types defined in C don't have a __dict__ attribute by
default.
As noted in this question.
The documentation for Python 2 is not very helpful in giving an explanation as to why you cannot assign attributes to an object(), but the documentation for Python 3 provides a bit more information:
Return a new featureless object. object is a base for all classes. It has the methods that are common to all instances of Python classes. This function does not accept any arguments.
Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.
Thus, the reason you cannot add arbitrary attributes to your object() appears to be because of the fact that object() instances do not have an implementation of the __dict__ attribute, not because object() instances are immutable:
>>> hasattr(object(), '__dict__')
False
>>>
Another interesting thing, but perhaps not relevant to the discussion at hand, is that while an instance of object may not have a __dict__ implementation, the object class itself does:
>>> hasattr(object, '__dict__')
True
As for the why part of the question, I cannot find any exact reasons for why object() doesn't have a __dict__. Is is probably because - as #tdelany has already mentioned on in the comments - an implementation detail. If you really want a definitive answer, you should ask Guido himself.
Related
Class definition:
class A(object):
def foo(self):
print "A"
class B(object):
def foo(self):
print "B"
class C(A, B):
def foo(self):
print "C"
Output:
>>> super(C)
<super: <class 'C'>, NULL>
>>> super(C).foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'foo'
What is the use of super(type) if we can't access attributes of a class?
super(type) is an "unbound" super object. The docs on super discuss that, but don't really elaborate what an "unbound" super object is or does. It is simply a fact of the language that you cannot use them in the manner you are attempting to use them.
This is perhaps what you want:
>>> super(C, C).foo is B.foo
True
That said, what good is an unbound super object? I had to look this up, myself, and found a decent answer here. Note however, that the article's conclusion is that unbound super is a language wart, has no practical use, and should be removed from the language (and having read the article, I agree). The article's explanation on unbound super starts with:
Unbound super objects must be turned into bound objects in order to make them to dispatch properly. That can be done via the descriptor protocol. For instance, I can convert super(C1) in a super object bound to c1 in this way:
>>> c1 = C1()
>>> boundsuper = super(C1).__get__(c1, C1) # this is the same as super(C1, c1)
So, that doesn't seem useful, but the article goes on:
Having established that the unbound syntax does not return unbound methods one might ask what its purpose is. The answer is that super(C) is intended to be used as an attribute in other classes. Then the descriptor magic will automatically convert the unbound syntax in the bound syntax. For instance:
>>> class B(object):
... a = 1
>>> class C(B):
... pass
>>> class D(C):
... sup = super(C)
>>> d = D()
>>> d.sup.a
1
This works since d.sup.a calls super(C).__get__(d,D).a which is turned into super(C, d).a and retrieves B.a.
There is a single use case for the single argument syntax of super that I am aware of, but I think it gives more troubles than advantages. The use case is the implementation of autosuper made by Guido on his essay about new-style classes.
The idea there is to use the unbound super objects as private attributes. For instance, in our example, we could define the private attribute __sup in the class C as the unbound super object super(C):
>>> C._C__sup = super(C)
But do note that the article continues to describe the problems with this (it doesn't quite work correctly, I think mostly due to the fact that the MRO is dependent on the class of the instance you are dealing with, and thus given an instance, some class X's superclass may be different depending on the instance of X we are given).
To accomplish what you want, you need to call it like this:
>>> myC = C()
>>> super(C,myC).foo()
A
Note that there is a NULL reference to some object. It basically needs a class and an instance of a related object for it to function.
>>> super(C, C()).foo
<bound method C.foo of <__main__.C object at 0x225dc50>>
See: Understanding Python super() with __init__() methods for more details.
This question already has answers here:
Why can't you add attributes to object in python? [duplicate]
(2 answers)
Closed 9 years ago.
I was trying out dynamic attribute assignment for testing purposes and discovered following behavior:
>>> class Foo(object): pass
...
>>> bar = Spam()
>>> bar.a = 1
>>> spam = object()
>>> spam.a = 2
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'object' object has no attribute 'a'
Why is the first version with a derived class legit, but the second direct usage of object not? It seems a bit strange to me because deriving hasn't changed anything that has obviously something to do with how variable assignment is handled.
That's because object is a native type, meaning that it's implemented in C code and does not support dynamic attribute assignment, for performance reasons. The same can be said for most Python native classes, such as str or int.
But Python allows you to subclass any native type and your subclasses do support dynamic assignment.
You can disable it for performance reasons on your classes too, using the __slots__ special attribute.
object instances don't have a __dict__.
>>> hasattr(object(), '__dict__')
False
And therefore can't have any attributes added to them.
I find the following example mildly surprising:
>>> class Foo:
def blah(self):
pass
>>> f = Foo()
>>> def bar(self):
pass
>>> Foo.bar = bar
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x02D18FB0>>
I expected the bound method to be associated with each particular instance, and to be placed in it at construction. It seems logical that the bound method would have to be different for each instance, so that it knows which instance to pass in to the underlying function - and, indeed:
>>> g = Foo()
>>> g.blah is f.blah
False
But my understanding of the process is clearly flawed, since I would not expect assigning a function into a class attribute would put it in instances that had already been created by then.
So, my question is two fold -
Why does assigning a function into a class apply retroactively to instances? What are the actual lookup rules and processes that make this so?
Is this something guaranteed by the language, or just something that happens to happen?
You want to blow your mind, try this:
f.blah is f.blah
That's right, the instance method wrapper is different each time you access it.
In fact an instance method is a descriptor. In other words, f.blah is actually:
Foo.blah.__get__(f, type(f))
Methods are not actually stored on the instance; they are stored on the class, and a method wrapper is generated on the fly to bind the method to the instance.
The instances do not "contain" the method. The lookup process happens dynamically at the time you access foo.bar. It checks to see if the instance has an attribute of that name. Since it doesn't, it looks on the class, whereupon it finds whatever attribute the class has at that time. Note that methods are not special in this regard. You'll see the same effect if you set Foo.bar = 2; after that, foo.bar will evalute to 2.
What is guaranteed by the language is that attribute lookup proceeds in this fashion: first the instance, then the class if the attribute is not found on the instance. (Lookup rules are different for special methods implicitly invoked via operator overloading, etc..)
Only if you directly assign an attribute to the instance will it mask the class attribute.
>>> foo = Foo()
>>> foo.bar
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
foo.bar
AttributeError: 'Foo' object has no attribute 'bar'
>>> foo.bar = 2
>>> Foo.bar = 88
>>> foo.bar
2
All of the above is a separate matter from bound/unbound methods. The class machinery in Python uses the descriptor protocol so that when you access foo.bar, a new bound method instance is created on the fly. That's why you're seeing different bound method instances on your different objects. But note that underlyingly these bound methods rely on the same code object, as defined by the method you wrote in the class:
>>> foo = Foo()
>>> foo2 = Foo()
>>> foo.blah.im_func.__code__ is foo2.blah.im_func.__code__
True
It's a thing that bugged me for a while. Why can't I do:
>>> a = ""
>>> a.foo = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'foo'
...while I can do the following?
>>> class Bar():
... pass
...
>>> a = Bar()
>>> a.foo = 10 #ok!
What's the rule here? Could you please point me to some description?
You can add attributes to any object that has a __dict__.
x = object() doesn't have it, for example.
Strings and other simple builtin objects also don't have it.
Classes using __slots__ also do not have it.
Classes defined with class have it unless the previous statement applies.
If an object is using __slots__ / doesn't have a __dict__, it's usually to save space. For example, in a str it would be overkill to have a dict - imagine the amount of bloat for a very short string.
If you want to test if a given object has a __dict__, you can use hasattr(obj, '__dict__').
This might also be interesting to read:
Some objects, such as built-in types and their instances (lists, tuples, etc.) do not have a __dict__. Consequently user-defined attributes cannot be set on them.
Another interesting article about Python's data model including __dict__, __slots__, etc. is this from the python reference.
In python, it is illegal to create new attribute for an object instance like this
>>> a = object()
>>> a.hhh = 1
throws
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'hhh'
However, for a function object, it is OK.
>>> def f():
... return 1
...
>>> f.hhh = 1
What is the rationale behind this difference?
The reason function objects support arbitrary attributes is that, before we added that feature, several frameworks (e.g. parser generator ones) were abusing function docstrings (and other attribute of function objects) to stash away per-function information that was crucial to them -- the need for such association of arbitrary named attributes to function objects being proven by example, supporting them directly in the language rather than punting and letting (e.g.) docstrings be abused, was pretty obvious.
To support arbitrary instance attributes a type must supply every one of its instances with a __dict__ -- that's no big deal for functions (which are never tiny objects anyway), but it might well be for other objects intended to be tiny. By making the object type as light as we could, and also supplying __slots__ to allow avoiding per-instance __dict__ in subtypes of object, we supported small, specialized "value" types to the best of our ability.
Alex Martelli posted an awesome answer to your question. For anyone who is looking for a good way to accomplish arbitrary attributes on an empty object, do this:
class myobject(object):
pass
o = myobject()
o.anything = 123
Or more efficient (and better documented) if you know the attributes:
class myobject(object):
__slots__ = ('anything', 'anythingelse')
o = myobject()
o.anything = 123
o.anythingelse = 456
The rationale is that an instance of object() is a degenerate special case. It "is" an object but it isn't designed to be useful by itself.
Think of object as a temporary hack, bridging old-style types and classes. In Python 3.0 it will fade into obscurity because it will no longer be used as part of
class Foo( object ):
pass
f = Foo()
f.randomAttribute = 3.1415926
Here's another alternative, as short as I could make it:
>>> dummy = type('', (), {})()
>>> dummy.foo = 5
>>> dummy.foo
5