Skip to content Skip to sidebar Skip to footer

Python Property Lookup With Custom __setattr__ And __slots__

I have a class that uses __slots__ and makes them nearly immutable by overriding __setattr__ to always raise an error: class A: __slots__ = ['a', 'b', '_x'] def __init__(s

Solution 1:

The real issue is that I do not understand why there is no conflict between __slots__ and the property, but there is one between __setattr__ and the property.

Both __slots__ and property implement attribute lookup by providing a descriptor for the corresponding attribute(s). The presence of __slots__ prevents arbitrary instance attribute creation not by doing anything to __setattr__, but by preventing creation of a __dict__. property and other descriptors don't rely on an instance __dict__, so they're unaffected.

However, __setattr__ handles all attribute assignment, meaning that descriptor invocation is __setattr__'s responsibility. If your __setattr__ doesn't handle descriptors, descriptors won't be handled, and property setters won't be invoked.

is there another, more elegant workaround to this problem?

You could explicitly allow only properties:

classA:
    ...
    def__setattr__(self, name, value):
        ifnotisinstance(getattr(type(self), name, None), property):
            raise AttributeError("Can't assign to attribute " + name)
        super().__setattr__(name, value)

or you could explicitly reject assignment to slots, and delegate other attribute assignment to super().__setattr__:

classA:
    ...
    def__setattr__(self, name, value):
        ifisinstance(getattr(type(self), name, None), _SlotDescriptorType):
            raise AttributeError("Can't assign to slot " + name)
        super().__setattr__(name, value)

# Seems to be the same as types.MemberDescriptorType,# but the docs don't guarantee it.
_SlotDescriptorType = type(A.a)

Post a Comment for "Python Property Lookup With Custom __setattr__ And __slots__"