I have been playing with my code and I ended up with this:
class TestProperty:
def __init__(self,func):
self.func = func
def __set__(self,instance,value):
setattr(instance,self.func.__name__,value)
def __get__(self,instance,cls):
if instance is None:
return self
else:
return getattr(instance,self.func.__name__)
class John(object):
def __init__(self):
pass
@TestProperty
def TestProp(self):
print('This line won\'t be printed')
p = John()
p.TestProp = 99
print(p.TestProp)
__set__
__get__
Don't use getattr()
and setattr()
; you are triggering the descriptor again there! The descriptor handles all access to the TestProp
name, using setattr()
and getattr()
just goes through the same path as p.TestProp
would.
Set the attribute value directly in the instance.__dict__
:
def __get__(self,instance,cls):
if instance is None:
return self
try:
return instance.__dict__[self.func.__name__]
except KeyError:
raise AttributeError(self.func.__name__)
def __set__(self,instance,value):
instance.__dict__[self.func.__name__] = value
This works because you have a data descriptor; a data descriptor takes precedence over instance attributes. Access to p.TestProp
continues to use the descriptor object on the class even though the name 'TestProp'
exists in instance __dict__
.