【问题标题】:How to associate a property-like object with a dict value in python如何在python中将类似属性的对象与dict值相关联
【发布时间】:2013-12-31 21:31:58
【问题描述】:

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

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

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 就不需要知道键可以引用的值有什么特别之处。

【问题讨论】:

  • 看看norman。它应该做你想做的,并允许字段级验证。

标签: python dictionary properties setter


【解决方案1】:

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

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,因为这让我找到了一个临时解决方案。您提供的见解是将@property 应用于字典级别。在伪代码中,实现是for each key, try to getattr(self, "_g/set_" + key), except AttributeError set the value using normal dict assignment。这使我可以从简单的分配开始,然后在不接触字典或 API 对象的情况下任意添加访问器。理想情况下,我仍然希望将类似属性的对象分配给 dict,但这对用户来说看起来是一样的,并且只需要在我的私有方法中进行一些命名规则。
  • 你不能“分配属性”给一个字典——你可以,但它不会像你期望的那样工作。现在,与其问如何实施您认为的解决方案,您可以解释您的真正问题是什么,因为可能有更好的解决方案......
  • 问题:如果你有很多不同的属性(不仅仅是头发颜色、眼睛颜色等),它们都具有相同的 getter 和 setter 逻辑,那么描述符那么是首选?
  • @SebastianNielsen 当然——这就是我发布这样一个例子的原因。
【解决方案2】:

如果你使用self._desc['haircolor']获取/设置它的值,它会调用dict的__getitem__()/__setitem__()方法,不管这个值是否是一个描述符。

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

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

【讨论】:

  • 我不反对重写 getitem,但我不确定这将如何实现我的目标。我可以看到这样的工作:
【解决方案3】:

如前所述: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

【讨论】:

    【解决方案4】:

    我有一个类似的案例并使用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'
    

    将打印“不允许”。因为值 'purple' 不符合约束。

    p.eyes = 1
    

    将“眼睛”设置为 1。

    【讨论】:

      猜你喜欢
      • 2021-07-24
      • 1970-01-01
      • 2011-04-05
      • 2013-09-06
      • 2017-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多