2016-11-02 4 views
0

Я пишу класс, который в какой-то момент добавляет свойства чтения/записи динамически. Моя первая попытка былаДинамическое добавление свойства не работает должным образом

class MyGPIO: 
    def configure(self): 
     gpio_list = [ "a", "b", "c" ] # only an example here 
     input_list = [ "b" ] 

     ... 
     for gpio in gpio_list: 
      getter = lambda obj: obj.get_gpio(gpio) 
      setter = lambda obj,val: obj.set_gpio(gpio, val) 

      if gpio in input_list: 
       setter = None 

      setattr(self.__class__, gpio, property(getter, setter)) 

    def get_gpio(self, name): 
     print "getting %s" % name 
     return True 

    def set_gpio(self, name, val): 
     print "setting %s=%s" % (name, val) 

, но потом я побежал в проблему области видимости, как описано здесь https://eev.ee/blog/2011/04/24/gotcha-python-scoping-closures/, поэтому я изменил код

def configure(self): 
     ... 
     for gpio in gpio_list: 
      def getset(gpio): 
       getter = lambda obj: obj.get_gpio(gpio) 
       setter = lambda obj,val: obj.set_gpio(gpio, val) 

       if gpio in input_list: 
        setter = None 

       setattr(self.__class__, gpio, property(getter, setter)) 

      getset(gpio) 

Это похоже на работу, как вы можете видеть из этого ipython сессия (magnet в gpio_list)

In [1]: gpio = MyGPIO() 
In [2]: gpio.configure(...) 

In [3]: gpio.magnet 
getting magnet 
Out[3]: True 

In [4]: gpio.magnet = False 

In [5]: gpio.magnet 
Out[5]: False 

первый раз доступ к magnet ре ad, вызывается функция get_gpio. Однако при доступе к свойству записи set_gpio игнорируется (или, скорее, setter лямбда в getset).

Я проверил это:

In [6]: def setme(obj,x): 
    ...:  print "obj=%s,x=%s" % (obj,x) 
    ...:  

In [7]: class A(object): 
    ...:  pass 
    ...: 

In [8]: A.a = property(None, lambda obj,x: setme(obj,x)) 

In [9]: a = A() 

In [10]: a.a = "test" 
obj=<__main__.A object at 0x7ff544028790>,x=test 

здесь он работает, как я предполагал. Так почему же он не работает с моим примером выше?

+3

ли вы наследуете объект от объекта (прямо или косвенно)? свойства могут иметь странное поведение с классами старого стиля Python2. Когда вы используете свойства, ваши классы должны наследоваться от объекта. https://wiki.python.org/moin/NewClassVsClassicClass – Tryph

+0

Мой класс наследуется от 'traits.api.HasTraits' – Pablo

ответ

1

Как отметил @Tryph в комментариях в Python2, это проблема между старым стилем и новыми классами стиля. Разница в классе нового стиля заключается в том, что ваш класс muss наследуется от object.

script1.py - старый стиль

class MyGPIO(object): 
    def configure(self): 
     gpio_list = [ "magnet" ,] # only an example here 
     input_list = [ "b" ] 
     for gpio in gpio_list: 
      def getset(gpio): 
       getter = lambda obj: obj.get_gpio(gpio) 
       setter = lambda obj,val: obj.set_gpio(gpio, val) 

       if gpio in input_list: 
        setter = None 

       setattr(self.__class__, gpio, property(getter, setter)) 

      getset(gpio) 

    def get_gpio(self, name): 
     print "getting %s" % name 
     return True 

    def set_gpio(self, name, val): 
     print "setting %s=%s" % (name, val) 
gpio = MyGPIO() 
gpio.configure() 
gpio.magnet 
gpio.magnet = False 

выход 1

getting magnet 

script2.py - новый класс стиля (унаследовав от object)

class MyGPIO(object): 
    def configure(self): 
     gpio_list = [ "magnet" ,] # only an example here 
     input_list = [ "b" ] 
     for gpio in gpio_list: 
      def getset(gpio): 
       getter = lambda obj: obj.get_gpio(gpio) 
       setter = lambda obj,val: obj.set_gpio(gpio, val) 

       if gpio in input_list: 
        setter = None 

       setattr(self.__class__, gpio, property(getter, setter)) 

      getset(gpio) 

    def get_gpio(self, name): 
     print "getting %s" % name 
     return True 

    def set_gpio(self, name, val): 
     print "setting %s=%s" % (name, val) 
gpio = MyGPIO() 
gpio.configure() 
gpio.magnet 
gpio.magnet = False 

выхода 2

getting magnet 
setting magnet=False 

Для получения дополнительной информации о старых и новых классах стилей и их воздействии на дескрипторы (в основном, что вы делаете), посмотрите на https://wiki.python.org/moin/NewClassVsClassicClass.

+0

Спасибо, похоже, что' traits' генерирует свои классы, тип 'property' не так хорошо поддерживается. 'traits' делает свою собственную« магию », и это может мешать типу' property'. Возможно, именно поэтому декораторы работают. – Pablo

2

Классы старого стиля (классы, которые явно не наследуются от object, напрямую или нет) не работают со свойствами.


Рассмотрим этот пример кода с использованием класса нового стиля:

class Test(object): 
    def __init__(self): 
     self.random_attribute = "random value" 

    @property 
    def randattr(self): 
     print("getter called") 
     return self.random_attribute 

    @randattr.setter 
    def randattr(self, value): 
     print("setter called") 
     self.random_attribute = value 

t = Test() 
print(t.randattr) 
t.randattr = "an other value" 
print(t.randattr) 

печатает:

getter called 
random value 
setter called 
getter called 
an other value 

Он работает, как ожидалось:

  • он вызывает добытчика, когда мы получить доступ к значению
  • он вызывает сеттер, когда мы устанавливаем значение
  • установленное значение correclty доступ

Рассмотрим теперь тот же самый код, кроме того, что класс не наследует от объекта:

class Test: 
    def __init__(self): 
     self.random_attribute = "random value" 

    @property 
    def randattr(self): 
     print("getter called") 
     return self.random_attribute 

    @randattr.setter 
    def randattr(self, value): 
     print("setter called") 
     self.random_attribute = value 

t = Test() 
print(t.randattr) 
t.randattr = "an other value" 
print(t.randattr) 

печатает:

getter called 
random value 
an other value 

Это сделать эс не работает:

  • геттер вызывается, когда мы достигаем значение
  • сеттер не вызывается, когда мы устанавливаем значение, это означает, что значение фактически стереть свойство, и это будет не больше работы по назначению ,
  • Геттер больше не вызывается, когда мы снова обращаемся к значению.

HasTraits класс наследует от CHasTraits, который исходит из двоичного модуля. Так что трудно что-либо заключить.


Это одна из проблем, вызванных классами старого стиля, но не единственными. В общем, в Python 2.7 это хорошая практика, чтобы всегда явно наследовать от объекта (если вы не наследуете от класса, который наследует от объекта).

+0

Из-за того же поведения со старыми классами я предполагаю, что 'traits' использует класс старого стиля. Странно то, что когда я использую декораторы, то это работает без проблем). – Pablo

Смежные вопросы