Python属性作为实例属性

2024-07-05 14:31:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图编写一个具有动态属性的类。考虑以下具有两个只读属性的类:

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

我想对此进行推广,以便__init__可以使用任意字典并从字典中的每个项创建只读属性。我可以这样做:

^{pr2}$

但是,这有一个严重的缺点:每次我创建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.__class__,key,property(lambda self,key=key: self._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>

所以我的问题是:“实例属性”是否存在?如果没有,原因是什么?我已经找到了很多关于Python中如何使用属性的信息,但是关于它们是如何实现的却很少。如果“实例属性”没有意义,我想了解原因。在


Tags: 实例keyself属性defpropertycolorhasattr
3条回答

So my question is this: do "instance properties" exist?

没有

If not, what is the reason?

因为属性被实现为descriptors。描述符的神奇之处在于,当在对象类型的字典中找到时,它们所做的事情与在对象字典中找到时的不同。在

I have been able to find lots about how properties are used in Python, but precious little about how they are implemented.

阅读上面链接的描述指南。在


那么,你有没有办法做到这一点?在

嗯,是的,如果你愿意重新考虑一下设计。在

对于您的例子,您所要做的就是使用_traits代替{},并且动态生成无用的getter函数,因此可以用几行代码替换整个过程,就像Martijn Pieters的回答一样。在

或者,如果您想将.foo重定向到._foo如果foo_traits的列表(或更好的情况下,集合)中,这同样简单:

def __getattr__(self, name):
    if name in self._traits:
        return getattr(self, '_' + name)
    raise AttributeError

但是假设您实际上对getter函数有某种用途,每个属性实际上需要一些代码来生成值,这个值已经封装在一个函数中,并存储在_traits中。在这种情况下:

^{pr2}$

What I need here instead are properties that can be added to the instance.

一个property()是一个描述符,它只在存储在类中时有效,而在实例中存储时不起作用。在

实现实例属性效果的一个简单方法是do def\u getattr\。这将允许您控制查找的行为。在

不,不存在按实例属性的事情;与所有描述符一样,属性总是总是在类中查找。请参见descriptor HOWTO了解具体的工作原理。在

您可以使用__getattr__钩子来实现动态属性,该钩子可以动态检查实例属性:

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)

但这些属性并不是真正的动态的;您可以直接在实例上设置这些属性:

^{pr2}$

相关问题 更多 >