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。