sun qingyao sun qingyao - 1 year ago 68
Python Question

Why favor object.__setattr__(self, name, value) only in new style classes?

According to Python 2.7.12 documentation:

wants to assign to an instance attribute, it should
not simply execute = value
— this would cause a recursive
call to itself. Instead, it should insert the value in the dictionary
of instance attributes, e.g.,
self.__dict__[name] = value
. For
new-style classes, rather than accessing the instance dictionary, it
should call the base class method with the same name, for example,
object.__setattr__(self, name, value)

However, the following code works as one would expect:

class Class(object):
def __setattr__(self, name, val):
self.__dict__[name] = val;

c = Class()
c.val = 42
print c.val

I know
super(Class, obj).__setattr__(name, value)
can ensure the
methods of all base classes to be called, but classic class can also inherit from bases classes. So why is it only recommended for new style classes?

Or, on the other hand, why is doing so not recommended for classic classes?

Answer Source

New-style classes could be using slots, at which point there is no __dict__ to assign to. New-style classes also support other data descriptors, objects defined on the class that handle attribute setting or deletion for certain names.

From the documentation on slots:

By default, instances of both old and new-style classes have a dictionary for attribute storage. This wastes space for objects having very few instance variables. The space consumption can become acute when creating large numbers of instances.

The default can be overridden by defining __slots__ in a new-style class definition. The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

Access to slots is instead implemented by adding data descriptors on the class; an object with __set__ and / or __del__ methods for each such attribute.

Another example of data descriptors are property() objects that have a setter or deleter function attached. Setting a key with the same name as such a descriptor object in the __dict__ would be ignored as data descriptors cause attribute lookup to bypass the __dict__ altogether.

object.__setattr__() knows how to handle data descriptors, which is why you should just call that.