如何在__init__中定义属性

时间:2009-09-21 15:04:03

标签: python properties constructor

我想从成员函数中定义类中的属性。 下面是一些测试代码,显示了我希望如何工作。但是我没有得到预期的行为。

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...> 

我怎么能让这个工作?

3 个答案:

答案 0 :(得分:12)

您需要在类上设置属性(即: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"

对于它的价值,在循环中创建lamdas时使用p并不会给出您期望的行为。由于p的值在循环中被更改,因此循环中设置的两个属性都返回相同的值:p的最后一个值。

答案 1 :(得分: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 

答案 2 :(得分:0)

为什么要在__init__时定义属性?它令人困惑和聪明,所以你最好有一个很好的理由。 Stef指出的循环问题只是为什么应该避免这种情况的一个例子。

如果需要重新定义子类具有哪些属性,则可以在子类del self.<property name>方法中执行__init__,或在子类中定义新属性。

另外,一些风格的挑剔:

  • 缩进到4个空格,而不是2个
  • 不要不必要地混合报价类型
  • 使用下划线代替驼峰大小写的方法名称。 PropNames - &gt; prop_names
  • PropNames并不一定是一种方法