Say I have a class which defines __slots__
:
class Foo(object):
__slots__ = ['x']
def __init__(self, x=1):
self.x = x
# will the following work?
def __setattr__(self, key, value):
if key == 'x':
object.__setattr__(self, name, -value) # Haha - let's set to minus x
Can I define __setattr__()
for it?
Since Foo
has no __dict__
, what will it update?
__slots__ is a class variable. If you have more than one instance of your class, any change made to __slots__ will show up in every instance. You cannot access the memory allocated by the __slots__ declaration by using subscription. You will get only what is currently stored in the list.
"You would want to use __slots__ if you are going to instantiate a lot (hundreds, thousands) of objects of the same class." Abstract Base Classes, for example, from the collections module, are not instantiated, yet __slots__ are declared for them.
Slots in Python is a special mechanism that is used to reduce memory of the objects. In Python, all the objects use a dynamic dictionary for adding an attribute. Slots is a static type method in this no dynamic dictionary are required for allocating attribute.
Python's magic method __setattr__() implements the built-in setattr() function that takes an object and an attribute name as arguments and removes the attribute from the object. We call this a “Dunder Method” for “Double Underscore Method” (also called “magic method”).
All your code does, apart from negate the value, is call the parent class __setattr__
, which is exactly what would happen without your __setattr__
method. So the short answer is: Sure you can define a __setattr__
.
What you cannot do is redefine __setattr__
to use self.__dict__
, because instances of a class with slots do not have a __dict__
attribute. But such instances do have a self.x
attribute, it's contents are just not stored in a dictionary on the instance.
Instead, slot values are stored in the same location a __dict__
instance dictionary would otherwise be stored; on the object heap. Space is reserved for len(__slots__)
references, and descriptors on the class access these references on your behalf.
So, in a __setattr__
hook, you can just call those descriptors directly instead:
def __setattr__(self, key, value):
if key == 'x':
Foo.__dict__[key].__set__(self, -value)
Interesting detour: yes, on classes without a __slots__
attribute, there is a descriptor that would give you access to the __dict__
object of instances:
>>> class Bar(object): pass
...
>>> Bar.__dict__['__dict__']
<attribute '__dict__' of 'Bar' objects>
>>> Bar.__dict__['__dict__'].__get__(Bar(), Bar)
{}
which is how normal instances can look up self.__dict__
. Which makes you wonder where the Bar.__dict__
object is found. In Python, it is turtles all the way down, you'd look that object up on the type
object of course:
>>> type.__dict__['__dict__']
<attribute '__dict__' of 'type' objects>
>>> type.__dict__['__dict__'].__get__(Bar, type)
dict_proxy({'__dict__': <attribute '__dict__' of 'Bar' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': None})
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