2009-09-21 71 views
4

我很想從成員函數中定義類的屬性。 下面是一些測試代碼,顯示我希望如何工作。但是我沒有得到預期的行爲。如何在__init__中定義屬性

class Basket(object): 

    def __init__(self): 
    # add all the properties 
    for p in self.PropNames(): 
     setattr(self, p, property(lambda : p)) 

    def PropNames(self): 
    # The names of all the properties 
    return ['Apple', 'Pear'] 

    # normal property 
    Air = property(lambda s : "Air") 

if __name__ == "__main__": 
    b = Basket() 
    print b.Air # outputs: "Air" 
    print b.Apple # outputs: <property object at 0x...> 
    print b.Pear # outputs: <property object at 0x...> 

我怎麼能得到這個工作?

回答

10

您需要設置類的屬性(即:self.__class__),而不是在對象上(即:self)。例如:

class Basket(object): 

    def __init__(self): 
    # add all the properties 
    setattr(self.__class__, 'Apple', property(lambda s : 'Apple')) 
    setattr(self.__class__, 'Pear', property(lambda s : 'Pear')) 

    # normal property 
    Air = property(lambda s : "Air") 

if __name__ == "__main__": 
    b = Basket() 
    print b.Air # outputs: "Air" 
    print b.Apple # outputs: "Apple" 
    print b.Pear # outputs: "Pear" 

對於它的價值,你的p使用在循環創建lamdas的時候,不給你所期望的行爲。由於p的值在經過循環時發生了變化,因此循環中設置的兩個屬性都返回相同的值:最後一個值爲p

3

這確實想要的東西:

class Basket(object): 
    def __init__(self): 
    # add all the properties 

    def make_prop(name): 
     def getter(self): 
      return "I'm a " + name 
     return property(getter) 

    for p in self.PropNames(): 
     setattr(Basket, p, make_prop(p)) 

    def PropNames(self): 
    # The names of all the properties 
    return ['Apple', 'Pear', 'Bread'] 

    # normal property 
    Air = property(lambda s : "I'm Air") 

if __name__ == "__main__": 
    b = Basket() 
    print b.Air 
    print b.Apple 
    print b.Pear 

另一種方式來做到這將是一個元類......但他們迷惑了很多人^^。

因爲我很無聊:

class WithProperties(type): 
    """ Converts `__props__` names to actual properties """ 
    def __new__(cls, name, bases, attrs): 
     props = set(attrs.get('__props__',())) 
     for base in bases: 
      props |= set(getattr(base, '__props__',())) 

     def make_prop(name): 
      def getter(self): 
       return "I'm a " + name 
      return property(getter) 

     for prop in props: 
      attrs[ prop ] = make_prop(prop) 

     return super(WithProperties, cls).__new__(cls, name, bases, attrs)  

class Basket(object): 
    __metaclass__ = WithProperties 
    __props__ = ['Apple', 'Pear'] 

    Air = property(lambda s : "I'm Air") 

class OtherBasket(Basket): 
    __props__ = ['Fish', 'Bread'] 

if __name__ == "__main__": 
    b = Basket() 
    print b.Air 
    print b.Apple 
    print b.Pear 

    c = OtherBasket() 
    print c.Air 
    print c.Apple 
    print c.Pear 
    print c.Fish 
    print c.Bread 
+0

我不同意,正確的方式做這將是一個元類。元類對於這個來說太強大了,因爲這可以使用不那麼強大的方法來完成。 – 2009-09-21 15:33:57

+0

感謝您的更新。我其實從來沒有像Python中的Metaclasses一樣深入。也許這是一個很好的理由開始閱讀他們。 – pkit 2009-09-21 15:51:34

1

__init__時候你爲什麼要定義屬性?它很混亂,很聰明,所以你最好有一個很好的理由。 Stef指出的循環問題只是爲什麼應該避免的一個例子。

如果您需要重新定義子類的屬性,您可以在子類__init__方法中執行del self.<property name>,或者在子類中定義新的屬性。

此外,一些風格挑剔:

  • 縮進爲4個空格,而不是2
  • 不要混合引用類型不必要
  • 使用下劃線,而不是駱駝情況下方法名。 PropNames - >prop_names
  • PropNames並不真正需要的是一個方法
+0

主要原因是我可以從籃子中取消並重新定義PropNames。附:最初的示例代碼實際上是使用4個空格,但是當我將它複製到SO時,它失去了縮進,所以我重新縮進了2個空格。 – pkit 2009-09-21 15:47:08

+0

@pkit:編輯回答討論子類。 – 2009-09-21 16:30:02

相關問題