如何在python中将类似属性的对象与dict值相关联

时间:2013-12-13 07:12:06

标签: python dictionary properties setter

我正在使用dict对象来保存类中的实例值。我喜欢使用dict,因为我经常想要同时设置或获取一堆值,并且很容易通过列表或字典来完成此操作。例如,为了获得几个实例值,我可以传递一个[键列表]并返回{这些键的值:值}。据我所知,使用每个键的单独属性执行此操作需要跳过很多代码箍(如果可能的话)。

无论如何,我正在寻找这样的东西:

class SomeClass(object):
    def __init__(self):
        self._desc = {
            'hair': 'brown'
            'eyes': 'blue'
            'height': 1.55
        }

到目前为止,这一切都非常简单,但有些值需要特殊的“设置”条件。我的第一次尝试是这样的:

class SomeClass(object):
    def __init__(self):
        self._haircolor = 'blonde'
        self._eyecolor = 'green'
        self._desc = {
            'haircolor': self.haircolor
            'eyecolor': self.eyecolor
            'height': 1.55
        }

    @property
    self.haircolor(self):
        return self._haircolor

    @haircolor.setter
    self.haircolor(self, color):
        if color.lower() in ['blonde', 'brunette', 'brown', 'black', 'auburn', 'red']:
            self._haircolor = color
        else: raise ValueError()

    # Same type of property construct for eyecolor

这不起作用。 self._desc ['haircolor']会初始化为'blonde',但它与属性构造或self._haircolor属性没有动态关系。我尝试用

替换它
class SomeClass(object):
    def __init__(self):
        self._haircolor = 'blonde'
        self._eyecolor = 'green'
        self._desc = {
            'haircolor': property(self.get_haircolor, self.set_haircolor)
            'eyecolor': property(self.get_eyecolor, self.set_eyecolor)
            'height': 1.55
        }

    self.get_haircolor(self):
        return self._haircolor

    self.set_haircolor(self, color):
        if color.lower() in ['blonde', 'brunette', 'brown', 'black', 'auburn', 'red']:
            self._haircolor = color
        else: raise ValueError()

但是这种方式以不同的方式失败了。

print self._desc['haircolor'] 

将为属性对象返回 str ,并

self._desc['haircolor'] = 'brunette'

销毁了属性对象并将其替换为字符串。

我研究了一些类似的问题,例如this onethis one

然而,这两种情况都在词典层面运作。问题在于,当我定义自定义dict类时,我需要了解很多有关dict的知识,因为我需要预测键名并在 setitem (第一个示例)或dict级别中切换它们属性对象(第二个例子)。理想情况下,我想将setter与值对象本身相关联,因此dict不需要知道key可以引用的值有什么特别之处。

4 个答案:

答案 0 :(得分:3)

Python对象已经在dict中存储了属性,那么在这里使用另一个dict有什么意义呢?只需按常规方式存储属性(使用有意义的属性),并在需要时动态构建desc dict。

class SomeClass(object):

    HAIRCOLORS = set(['blonde', 'brunette', 'brown', 'black', 'auburn', 'red'])
    EYECOLORS = set(['green', 'blue', 'brown', 'gray'])

    def __init__(self, haircolor="blond", eyecolor="green"):
        self.haircolor = haircolor
        self.eyecolor = eyecolor
        self.height = 1.55 # you didn't mention any validation here

    @property
    def haircolor(self):
        return self._haircolor

    @haircolor.setter
    def haircolor(self, color):
        color = color.lower()
        if color not in self.HAICOLORS:
            raise ValueError("'%s' is not a valid hair color" % color)
        self._haircolor = color

    # same thing for eyecolor

    @property
    def desc(self):
        return dict(
           haircolor=self.haircolor, 
           eyecolor=self.eyecolor, 
           height=self.height)

如果这是一个重新安排模式,你最终可以编写自己的描述符:

class ChoiceDescriptor(object):
    def __init__(self, key, choices):
        self.key = key
        self.storage = "_%s" % key
        self.choices = set(choices)

    def __get__(self, instance, cls=None):
        if instance is None:
            return self
        return getattr(instance, self.storage)


    def __set__(self, instance, value):
        value = value.lower()
        if value not in self.choices:
            raise ValueError(
                "'%s' is not a valid value for '%s'" % (value, self.key))
        setattr(instance, self.storage, value)


class SomeClass(object):
    haircolor = ChoiceDescriptor("haircolor", 
        ['blonde', 'brunette', 'brown', 'black', 'auburn', 'red'])
    eyecolor = ChoiceDescriptor("eyecolor", 
        ['green', 'blue', 'brown', 'gray'])

答案 1 :(得分:1)

如果使用self._desc['haircolor']来获取/设置其值,它将调用dict的__getitem__() / __setitem__()方法,无论该值是否为描述符。

如果您想访问描述符,访问它的方法是使用类似instance.descrip的内容,因为此时它会调用__getattribute__()方法,如果描述是描述符,它将使用相应的settergetter方法,否则会将其视为普通属性。这是关于descriptor

的文档

您只想控制在dict中调用键的方式?您可以参考此问题Python: How to “perfectly” override a dict来覆盖类似dict的类中的__getitem__()__setitem__()方法。这并不复杂,您应该做的就是创建一个这样的类,然后使用它就好像它是一个 dict 类型变量。
之后,使用新的类似dict的类生成self._desc变量,并根据需要获取或设置self._desc['haircolor']之类的内容。

答案 2 :(得分:0)

如前所述:Python对象已经将属性存储在dict中,因此您可以在" desc"中代理它。

class SomeClass(object):
    def __init__(self, d):
        self.__dict__ = d

    @property
    def desc(self):
        return self.__dict__

你也可以从这个类继承,以更新对象:

class objdict(dict):
    def __getattr__(self, name):
        if name in self:
            return self[name]
        else:
            raise AttributeError("No such attribute: " + name)

    def __setattr__(self, name, value):
        self[name] = value

    def __delattr__(self, name):
        if name in self:
            del self[name]
        else:
            raise AttributeError("No such attribute: " + name)

或使用named tuples

答案 3 :(得分:0)

我有一个类似的案例并使用descriptor管理它:

class Description(object):
    """ Descriptor which gets/sets value from instances _desc attribute via key."""

    def __init__(self,key):
        self.key = key

    def __get__( self, instance, owner ):
        return instance._desc[self.key]

    def __set__( self, instance, value ):

        if self.key in instance._constraints:
            if value in instance._constraints[self.key]:
                instance._desc[self.key] = value
            else:
                print('Not allowed.')

        else:
            instance._desc[self.key] = value

class Person(object):

    _constraints = {'hair':['brown','blonde']}

    def __init__(self):
        self._desc = {
            'hair': 'brown',
            'eyes': 'blue',
            'height': 1.55,
        }

    hair = Description('hair')
    eyes = Description('eyes')
    height = Description('height')

现在我们可以:

p = Person()

p.hair = 'purple'

将打印'不允许。'因为价值'紫色'不符合约束条件。

p.eyes = 1

设置眼睛'到1。