如何在__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...>
我该怎么做才能让它正常工作呢?
3 个回答
0
你为什么要在__init__
的时候定义属性呢?这样做会让人感到困惑,而且显得很聪明,所以你最好有个很好的理由。Stef提到的循环问题就是一个例子,说明了为什么应该避免这样做。
如果你需要重新定义一个子类的属性,可以在子类的__init__
方法里用del self.<属性名>
来删除,或者在子类里定义新的属性。
另外,还有一些风格上的小建议:
- 缩进用4个空格,不要用2个
- 不要随便混用不同类型的引号
- 方法名用下划线,而不是驼峰命名法。比如
PropNames
应该改成prop_names
PropNames
其实不需要定义成一个方法
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
13
你需要在类上设置属性(也就是 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"
顺便提一下,你在循环中创建 lambda 函数时使用的 p
,并不会产生你想要的效果。因为在循环过程中,p
的值会不断变化,所以在循环中设置的两个属性最终都会返回相同的值:就是 p
的最后一个值。