Yassine Faris
Yassine Faris

Reputation: 991

Replace property attribute with a normal attribute

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 _xand 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

Answers (1)

Martijn Pieters
Martijn Pieters

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

Related Questions