Bartłomiej Hirsz
Bartłomiej Hirsz

Reputation: 48

How to decorate property in Python

I'm trying to add extra decorator for magic method (__get__) in descriptor class.

I'm able to do it when I use @property but not when I use descriptor class.

I check range because my object set registers on the bus and some registers can take only specific range of values:

import functools

def check_range(min, max):
    def decorator(f):
        @functools.wraps(f)
        def wrap(self, value):
            if value not in range(min, max+1):
               return
           return f(self, value)
        return wrap
    return decorator

This works:

class Foo:
   def __init__(self):
      self.device.init_smth('my_object')

   @property
   def my_object(self):
      return self.device.get_value('my_object')

   @my_object.setter
   @check_range(0,1)
   def my_object(self, value):
       self.device.set_value('my_object', value)

a = Foo()
print(a.my_object)
a.my_object = 1
print(a.my_object)
a.myobject = -1

And in this example everything works the same but check_range is not invoked:

class Register:
    def __init__(self, name, device):
        self.name = name
        device.init_smth(name)

    def __get__(self, instance, owner):
        return instance.device.get_value(self.name)

    @check_range(0,1)
    def __set__(self, instance, value):
        instance.device.set_value(self.name, value)

class Foo:
   def __init__(self):
      self.my_object = Register('my_object', self.device)

a = Foo()
print(a.my_object)
a.my_object = 1
print(a.my_object)
a.myobject = -1

Upvotes: 2

Views: 1712

Answers (1)

Slam
Slam

Reputation: 8572

I may be wrong, but most probably your descriptor not invoked at all, decorator is not the problem. Descriptors meant to be used like

class Foo2:
    my_object = Register('my_object', 'init_value')

— you're defining it like class attribute. And python will execute all machinery with __get__/__set__/__del__ if your class attribute supports it (i.e. it is descriptor).

This is why there is an "instance" argument in descriptor methods — you're defining descriptor as class variable, but i.e. __set__ method will receive actual instance of your class, so you can manage per-instance data, like your device

Upvotes: 1

Related Questions