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