Today I stumbled upon the following behaviour:
class myobject(object):
"""Should behave the same as object, right?"""
obj = myobject()
obj.a = 2 # <- works
obj = object()
obj.a = 2 # AttributeError: 'object' object has no attribute 'a'
I want to know what is the logic behind designing the language to behave this way, because it feels utterly paradoxical to me. It breaks my intuition that if I create a subclass, without modification, it should behave the same as the parent class.
EDIT: A lot of the answers suggest that this is because we want to be able to write classes that work with __slots__ instead of __dict__ for performance reasons. However, we can do:
class myobject_with_slots(myobject):
__slots__ = ("x",)
obj = myobject_with_slots()
obj.x = 2
obj.a = 2
assert "a" in obj.__dict__ # ✔
assert "x" not in obj.__dict__ # ✔
So it seems we can have both __slots__ and __dict__ at the same time, so why doesn't object allow both, but one-to-one subclasses do?
Consider this code:
class A:
__slots__ = ()
class B(A):
__slots__ = ("x", "y")
b = B()
b.z = 1 # AttributeError
__slots__ = ("x", "y") means that B instances don't have a __dict__ and can only have attributes x and y. This is good for performance.
If you remove the __slots__ from A, then A instances get a __dict__, which means so do their subclasses, particularly B. This reduces the effectiveness of __slots__ at improving performance.
Because object is a superclass of all classes, you can think of it like A here, having __slots__ = () and no __dict__ so that other classes can also avoid having a __dict__ and fully benefit from custom __slots__.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With