Restrict __setattr__ when the attribute does not exist within the instance in Python

0

Hello, I need to know how I can assemble a class that can not have attributes implemented after the instance. Ex:

class Foo(object)

    def __init__(self, a='hello', b='world')
        self.a = a
        self.b = b

    __setattr__(self, key, value)
        if hasattr(self, key):
            super().__setattr__(key, value)
        else:
            pass

I'm trying to create a class like this but it does not work because even me checking if the attribute exists in the instance class still sets a new attribute, rather than just ignoring ...

    
asked by anonymous 06.09.2016 / 19:05

2 answers

5

You may not need to declare __setattr__ - The Python object model defines the special attribute __slots__ that, being declared in a class and in all classes superclasses, restricts the attributes that the objects of the class may have. ( link )

In addition to the desired constraint, the __slots__ attribute has the advantage that Python does not need to create an instance of a dictionary for each instance of the class (that's its main purpose actually) - thus making objects that would have the role of a small data structure have a very small memory footprint.

class Foo(object):

    __slots__ = ('a', 'b')

    def __init__(self, a='hello', b='world'):
        self.a = a
        self.b = b

And in the interactive environment:

>>> f= Foo()
>>> f.c = 5

    AttributeError                            Traceback (most recent call last)
    <ipython-input-82-6ce08c4facb1> in <module>()
    ----> 1 f.c = 5


AttributeError: 'Foo' object has no attribute 'c'
    
07.09.2016 / 11:43
1

You were on the right track, I think it's more of a syntax error problem than something else, see this example:

class Foo:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __setattr__(self, key, value):
        if not hasattr(self, key):
            return
        super().__setattr__(key, value)

if __name__ == '__main__':
    f = Foo('a', 'b')
    f.c = 'c'
    print(f.c)

It's basically the same code you wrote, with the difference that the __setattr__ method is written as a method correctly. When executing this code, a AttributeError: happens:

Traceback (most recent call last):
  File "attr.py", line 15, in <module>
    print(f.c)
AttributeError: 'Foo' object has no attribute 'c'

That is, the c attribute was not created on the object, it was ignored.

However, there is a side effect, by overriding the __setattr__ method with this behavior, no attribute is accepted by the class, ie not even declared in method __init__ ( a and b ).

A possible solution would be to declare a list of allowed attributes and to base it on that list, instead of checking the attributes that the object has:

class Foo:

    attributes = ('a', 'b')

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __setattr__(self, key, value):
        if key not in self.attributes:
            return
        super().__setattr__(key, value)
    
07.09.2016 / 05:51