Reputation: 991
Is it possible to replace a property of an object with 'normal' attribute?
I need this because when I access the attribute for the first, I want the value to be generated by the property. But I no longer need the property afterward:
class A(object):
@property
def x(self):
self.x = "toto" # Replace property, fail because no setter
return self.x
a = A()
print a.x # "toto"
a.x = "tata"
I know I can store the value in a second attribute like _x
and check in the property if _x
exist but I want to know if it's possible the replace the property itself.
Upvotes: 3
Views: 489
Reputation: 1121962
To bypass the lack of a setter, you'd have to directly manipulate the instance __dict__
dictionary. However, you can't do what you want with a regular property
object, because it is a data descriptor. Attribute access will always give a data descriptor priority over instance attributes.
You'd have to create a custom descriptor instead, one that doesn't define a __set__
or __delete__
method:
class CachingProperty(object):
def __init__(self, fget):
self.name = fget.__name__
self.fget = fget
def __get__(self, instance, owner):
if instance is None:
return self
value = self.fget(instance)
instance.__dict__[self.name] = value
return value
This descriptor also takes care of setting the value directly in the instance __dict__
attribute, thus creating an instance attribute.
Use the above class instead of property
:
class A(object):
@CachingProperty
def x(self):
return "toto"
Demo, showing that the getter method is only called once:
>>> class Demo(object):
... @CachingProperty
... def foo(self):
... print("Calling the foo property")
... return "bar"
...
>>> d = Demo()
>>> d.foo
Calling the foo property
'bar'
>>> d.foo
'bar'
>>> vars(d)
{'foo': 'bar'}
Upvotes: 3