6

Is there a preferred way of decorating a class property? As I see it you could either decorate the property itself with another property or you could decorate the underlying method and then apply @property on that.
Are there any considerations to make for either approach?

def decorate_property(prop):
    @property
    def inner(instance):
        return prop.__get__(instance) + 1
    return inner

def decorate_func(func):
    def inner(instance):
        return func(instance) +1
    return inner

class A:

    x = 1

    @decorate_property
    @property
    def f(self):
        return self.x

    @property
    @decorate_func
    def g(self):
        return self.x

a = A()
print(a.f) # 2
print(a.g) # 2
CoffeeBasedLifeform
  • 2,296
  • 12
  • 27
  • Check this out - https://www.programiz.com/python-programming/property – Justin Apr 25 '19 at 11:15
  • This too - https://www.codementor.io/sheena/advanced-use-python-decorators-class-function-du107nxsv – Justin Apr 25 '19 at 11:16
  • Possible duplicate of - https://stackoverflow.com/questions/2406567/preferred-way-of-defining-properties-in-python-property-decorator-or-lambda – Justin Apr 25 '19 at 11:17

1 Answers1

3

Properties are objects (descriptors) that wrap functions. Which one to decorate depends on which one you intend to change.

  1. If you want to change what the property does, decorate the initial function.

    @property
    @plus_one  # add one to the result of the function
    def a(self):
        return self._a
    

    This also applies to changing the setter and deleter.

    @property
    @plus_one  # add one to the result of the function
    def a(self):
        return self._a
    
    @a.setter
    @minus_one  # remove one from the input of the function
    def a(self, value):
        self._a = value
    
  2. If you want to change what the property is, decorate the resulting descriptor.

    @random_repr  # add a new repr to the descriptor
    @property
    def a(self):
        return 1
    

    Note that defining setter and deleter creates a new descriptor. You need to decorate the last descriptor created.

    @property
    def a(self):
        return self._a
    
    @random_repr  # add a new repr to the final descriptor
    @a.setter
    def a(self, value):
        self._a = value
    

For the most part, a property is used to get a certain behaviour expressed by a function (case 1). The data descriptor (case 2) is an implementation detail that most people are not familiar with. In case of doubt, decorate the function - this case is the more generic and easier to understand.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119