2016-11-02 200 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重新广告属性,get_gpio函数被调用。但是,当访问写入属性时,将忽略set_gpio(或中的setter lambda)。

我选中此:

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的评论中指出的那样,这是老风格和新风格类之间的问题。新式课程的不同之处在于,你的课堂教学从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

谢谢,似乎'特征'生成它的类,'属性'类型得不到很好的支持。 “特质”是它自己的“魔力”,这可能会阻碍“财产”类型。也许这就是装饰者工作的原因。 – 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 

它按预期工作:

  • 它调用getter方法时,我们


    使用新型类的考虑这个例子的代码获取价值

  • 它调用的时候,我们设置的值集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 
    

    它做ES不行:

    • 当我们访问值
    • ,当我们设置的值设置器不叫调用getter时,这意味着该值实际上抹去的财产,这将没有更多的工作,如预期。
    • 当我们再次访问该值时,getter不再被调用。

    HasTraits类从CHasTraits它来自二进制模块继承。所以很难总结任何事情。


    这是由旧式类引起的问题之一,但不是唯一的问题。 一般来说,在Python 2.7中,始终明确地从对象继承(除非您从继承自对象的类继承)是一个好习惯。

  • +0

    由于与旧式课程相同的行为,我假设'traits'使用旧式课程。奇怪的是,当我使用装饰器,然后它没有问题)。 – Pablo