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

Asked

Viewed 205 times

0

Hello, I need to know how to assemble a class that cannot 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 doesn’t work because even I’m checking if the attribute exists in the class the instance still defines a new attribute, rather than just ignoring...

2 answers

5


Maybe you don’t need to declare the __setattr__ - Python object model defines special attribute __slots__ which, being declared in one class and in all the superclasses, restricts the attributes that class objects may have. (https://docs.python.org/3/reference/datamodel.html#slots)

In addition to the desired restriction, the attribute __slots__ 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) - so making objects that would play the role of a small data structure have a well-reduced 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'
  • Before I used a condition variable and performed a function that required the __setattr__ to read if the variable was true and then locked the object to new attributes... using the slots really got less "feature-technical" rs

  • a problem I have identified in relation to __slots__ is that if I have "nested classes" defined within the class where they serve to complement a list, the element slots conflicts with the parent class

1

You were on the right track, I think it’s more of a syntax error problem than something else, take 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)

Basically it’s the same code you wrote, with the difference that the method __setattr__ is written as a method correctly. When executing this code, a AttributeError::

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 attribute c was not created in the object, it was ignored.

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

A possible solution would be to declare a list of permissible attributes and base on that list, rather than 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)
  • Very good answer, but take a look at the __slots__

  • I saw your reply @jsbueno really much better and more assertive. I have used slots in order to decrease the use of memory in creating thousands of similar objects, but never thought to use to restrict the attributes of a class.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.