Python属性作为实例属性

Emm*_*mma 4 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): return self._has_fur
Run Code Online (Sandbox Code Playgroud)

我想概括这一点,以便__init__可以使用任意字典并从字典中的每个项创建只读属性.我可以这样做:

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]))
Run Code Online (Sandbox Code Playgroud)

但是,这有一个严重的缺点:每次我创建一个新实例时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
Run Code Online (Sandbox Code Playgroud)

这当然是有道理的,因为我明确地将属性添加为类属性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>
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:"实例属性"存在吗?如果没有,原因是什么?我已经能够找到很多关于如何在Python中使用属性的内容,但关于如何实现它们却很少见.如果"实例属性"没有意义,我想了解原因.

Mar*_*ers 8

不,没有每个实例属性这样的东西; 像所有描述符一样,属性总是在类上查找.请参阅描述符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)
Run Code Online (Sandbox Code Playgroud)

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

class Monster(object):
    def __init__(self, traits):
        self.__dict__.update(traits)
Run Code Online (Sandbox Code Playgroud)