Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

*Why* does object() not support `setattr`, but derived classes do?

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?

like image 818
Hyperplane Avatar asked Jul 24 '26 01:07

Hyperplane


1 Answers

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__.

like image 78
Alex Hall Avatar answered Jul 26 '26 16:07

Alex Hall