Home > database >  Can not make property and __getattr__ working together
Can not make property and __getattr__ working together

Time:01-31

I am working on a python class that has declared properties, and in which I want to add extra attributes at object instanciation (passed in the init method). I want them to be read and written. Finally, I don't want the user to be able to declare custom attributes; it should raise an Error.

class Person:

    __slots__ = ["_name", "__dict__"]

    def __init__(self, name, extra_arg):
        self.__dict__[extra_arg] = None
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    def __getattr__(self, item):
        if item in self.__dict__:
            return self.__dict__[item]
        raise AttributeError(item)

person = Person("gribouille", "hello")

person.custom_attribute = value # I want to prevent this

In this example, I can't manage to prevent new attributes to be declared. When I override setattr method, it seems to collide with my property and I can't manage to retrieve my "name" attribute.

CodePudding user response:

How about checking for existing attributes via hasattr and __slots__?

class Person:

    __slots__ = ["_name", "__dict__"]

    def __init__(self, name, extra_arg):
        self.__dict__[extra_arg] = None
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    def __getattr__(self, item):
        if item in self.__dict__:
            return self.__dict__[item]
        raise AttributeError(item)
        
    def __setattr__(self, attr_name, attr_value):
        if not (hasattr(self, attr_name) or attr_name in self.__slots__):
            raise AttributeError(attr_name)
        super().__setattr__(attr_name, attr_value)

person = Person("gribouille", "hello")
person.name = "test"

person.custom_attribute = None # Now: AttributeError: custom_attribute

CodePudding user response:

person.custom_attribute = value # I want to prevent this

To achieve this your class should do NOT have __dict__ attribute, that is __slots__ must not contain __dict__. Consider following simple example

class C1:
    __slots__ = ["__dict__"]
class C2:
    __slots__ = ["x","y"]
c1 = C1()
c1.custom = "hello"
print(c1.custom) # hello
c2 = C2()
c2.x = 10
c2.y = 30
print(c2.x,c2.y) # 10 30
c2.z = 100 # cause AttributeError: 'C2' object has no attribute 'z'
  • Related