Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can __setattr__() can be defined in a class with __slots__?

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?

like image 222
bavaza Avatar asked Oct 24 '13 12:10

bavaza


People also ask

What is the __ slots __ attribute used in a class for?

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

Should I use __ slots __?

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

What are slots in Python?

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.

What is __ Setattr __?

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”).


1 Answers

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})
like image 110
Martijn Pieters Avatar answered Oct 05 '22 20:10

Martijn Pieters