Python属性作为实例属性
我正在尝试编写一个具有动态属性的类。考虑以下两个只读属性的类:Python属性作为实例属性,python,properties,Python,Properties,我正在尝试编写一个具有动态属性的类。考虑以下两个只读属性的类: class Monster(object): def __init__(self,color,has_fur): self._color = color self._has_fur = has_fur @property def color(self): return self._color @property def has_fur(self): retu
class Monster(object):
def __init__(self,color,has_fur):
self._color = color
self._has_fur = has_fur
@property
def color(self): return self._color
@property
def has_fur(self): return self._has_fur
我想对此进行概括,以便\uuuu init\uuu
可以获取任意字典,并从字典中的每个项创建只读属性。我可以这样做:
class Monster2(object):
def __init__(self,traits):
self._traits = traits
for key,value in traits.iteritems():
setattr(self.__class__,key,property(lambda self,key=key: self._traits[key]))
然而,这有一个严重的缺点:每次我创建一个新的Monster
实例时,我实际上都在修改Monster
类。我没有为新的Monster
实例创建属性,而是有效地向Monster
的所有实例添加属性。要了解这一点:
>>> hasattr(Monster2,"height")
False
>>> hasattr(Monster2,"has_claws")
False
>>> blue_monster = Monster2({"height":4.3,"color":"blue"})
>>> hasattr(Monster2,"height")
True
>>> hasattr(Monster2,"has_claws")
False
>>> red_monster = Monster2({"color":"red","has_claws":True})
>>> hasattr(Monster2,"height")
True
>>> hasattr(Monster2,"has_claws")
True
这当然是有意义的,因为我使用setattr(self.\uuuuu class\uuuuu,key,property(lambda self,key=key:self.\u traits[key]))显式地将属性添加为类属性。
。这里我需要的是可以添加到实例中的属性。(即“实例属性”)。不幸的是,根据我阅读和尝试的所有内容,属性总是类属性,而不是实例属性。例如,这不起作用:
class Monster3(object):
def __init__(self,traits):
self._traits = traits
for key,value in traits.iteritems():
self.__dict__[key] = property(lambda self,key=key: self._traits[key])
>>> green_monster = Monster3({"color":"green"})
>>> green_monster.color
<property object at 0x028FDAB0>
3类怪物(对象):
定义初始(自我,特征):
自我特质=特质
对于键,traits.iteritems()中的值:
self.\uuuu dict\uuuu[key]=属性(lambda self,key=key:self.\u traits[key])
>>>绿色怪物=怪物3({“颜色”:“绿色”})
>>>绿色
所以我的问题是:“实例属性”存在吗?若否,原因为何?我已经找到了很多关于在Python中如何使用属性的信息,但对于如何实现它们却知之甚少。如果“实例属性”没有意义,我想了解原因
所以我的问题是:“实例属性”存在吗
没有
若否,原因为何
因为属性实现为。描述符的神奇之处在于,当在对象类型的字典中找到描述符时,它们做的事情与在对象的字典中找到描述符时不同
我已经找到了很多关于在Python中如何使用属性的信息,但对于如何实现它们却知之甚少
阅读上面链接的说明如何操作指南
那么,你有没有办法做到这一点 是的,如果你愿意重新考虑一下设计的话 对于您的情况,您所要做的就是使用
\u traits
代替\u dict\u
,动态生成无用的getter函数,这样您就可以用几行代码替换整个过程,就像Martijn Pieters的回答一样
或者,如果您想将.foo
重定向到。\u foo
如果foo
在\u traits
的列表中(或者更好地说,是一组),那么同样简单:
def __getattr__(self, name):
if name in self._traits:
return getattr(self, '_' + name)
raise AttributeError
但假设您实际上对getter函数有某种用途,每个属性实际上都需要一些代码来生成值,这些值已封装在函数中,并存储在\u traits
中。在这种情况下:
def __getattr__(self, name):
getter = self._traits.get(name)
if getter:
return getter()
raise AttributeError
不,不存在按实例属性这样的东西;与所有描述符一样,属性总是在类上查找。具体如何工作,请参见 您可以使用
\uuu getattr\uuu
钩子来实现动态属性,钩子可以动态检查实例属性:
class Monster(object):
def __init__(self, traits):
self._traits = traits
def __getattr__(self, name):
if name in self._traits:
return self._traits[name]
raise AttributeError(name)
但这些属性并不是真正动态的;您可以直接在实例上设置这些:
class Monster(object):
def __init__(self, traits):
self.__dict__.update(traits)
这里我需要的是可以添加到实例中的属性
property()是一个描述符,这些描述符仅在存储在类中时有效,而在存储在实例中时无效
实现实例属性效果的一种简单方法是do def_ugetattr_uu。这将允许您控制查找的行为。如果您不需要将该属性设置为只读,您只需使用kwargs更新对象
class Monster(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
然后您可以这样创建该类的实例:
m0 = Monster(name='X')
m1 = Monster(name='godzilla', behaviour='godzilla behaviour')
另一种方法是动态创建怪物类。e、 g
def make_monster_class(traits):
class DynamicMonster(object):
pass
for key, val in traits.items():
setattr(DynamicMonster, key, property(lambda self, val=val: val))
return DynamicMonster()
blue_monster = make_monster_class({"height": 4.3, "color": "blue"})
red_monster = make_monster_class({"color": "red", "has_claws": True})
for check in ("height", "color", "has_claws"):
print "blue", check, getattr(blue_monster, check, "N/A")
print "red ", check, getattr(red_monster, check, "N/A")
输出:
blue height 4.3
red height N/A
blue color blue
red color red
blue has_claws N/A
red has_claws True
我不一定推荐这样做(通常最好使用
\uuu getattr\uuu
解决方案),但您可以编写类,以便由它生成的所有实例都有自己的类(当然,是它的子类)。这是该想法的一个快速黑客实现:
class MyClass(object):
def __new__(Class):
Class = type(Class.__name__, (Class,), {})
Class.__new__ = object.__new__ # to prevent infinite recursion
return Class()
m1 = MyClass()
m2 = MyClass()
assert type(m1) is not type(m2)
现在您可以轻松地在type(self)
上设置属性,因为每个实例都有自己的类对象
@Claudiu的答案也是一样,只是用一个函数实现,而不是集成到实例生成机制中。你为什么要避免使用子类?你为什么要这样做?如果所有getter函数都是从dict返回值的相同函数,为什么还要使用它们呢?将getter放在首位的唯一原因是,如果需要运行一些代码来生成值。@abarnert:属性也常用于创建只读变量,请注意缺少setter。你是对的,这是一个非常简单的例子,我的真实代码确实有更复杂的getter。