0

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?

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, just what I needed, but is there a way to use just one class? So the combination of FooMeta and Foo?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.