Skip to content
Advertisement

How to use __get__ and __set__ (python descriptors)

I’m new to using descriptors and I think I have a good understanding on how they work but I have come across a problem and i’m not sure how to fix it.

Code

class Foo:
    class Bar:
        def __get__(self,instance, owner):
            return 10
        def __set__(self,instance,value):
            raise Exception
    bar=Bar()

print(Foo.bar)
Foo.bar=5
print(Foo.bar)

Output

>>> 10
>>> 5

Im trying to make bar a constant for testing purposes, I know about the property decorator but I prefer using descriptors.

First I print out the value of bar to see if __get__ works – and it does, the output is 10.

But then when I assign 5 to bar the expected result would be an exception but instead what happens is 5 gets assigned to bar despite specifying __set__ so when I print again the second output is 5.

Can someone tell me why the error isn’t being raised?

Advertisement

Answer

From the docs:

object.__set__(self, instance, value)

Called to set the attribute on an instance instance of the owner class to a new value, value.

In your code, Foo.bar = 5 is setting the class attribute, not an instance attribute. If you do use an instance (without first setting Foo.bar = 5, which overrides your descriptor), then you get an exception as expected:

>>> f = Foo()
>>> f.bar
10
>>> f.bar = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __set__
Exception

If you want the __set__ behaviour to apply when the class attribute is set, then the class itself needs to be an instance of a metaclass which uses the descriptor:

class FooMeta(type):
    class Bar:
        def __get__(self,instance, owner):
            return 10
        def __set__(self,instance,value):
            raise Exception
    bar = Bar()

class Foo(metaclass=FooMeta):
    pass

Testing:

>>> Foo.bar
10
>>> Foo.bar = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __set__
Exception
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement