2016-11-02 208 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