How to associate a property-like object with a dict value in python
我正在使用dict对象在类中保存实例值。我喜欢听写,因为我经常想同时设置或获取一组值,并且很容易通过一个列表或听写来实现这一点。例如,要获得几个实例值,我只需传递一个[键列表]并返回这些键的dict:values。据我所知,如果可能的话,使用每个键的单独属性来完成这项工作需要跳过许多代码循环。
不管怎样,我在找这样的东西:
1 2 3 4 5 6 7 | class SomeClass(object): def __init__(self): self._desc = { 'hair': 'brown' 'eyes': 'blue' 'height': 1.55 } |
到这里为止,这一切都很简单,但有些值需要特殊的"设置"条件。我的第一次尝试是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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']将初始化为"bround",但它与属性构造或self.hairColor属性没有动态关联。我试着把这个换成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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() |
但这以另一种方式失败了。
1 | print self._desc['haircolor'] |
将返回属性对象的str,并且
1 | self._desc['haircolor'] = 'brunette' |
已销毁属性对象并将其替换为字符串。
我研究了一些类似的问题,像这个问题和这个问题。
不过,这两种情况都是在字典级别操作的。问题是,在定义自定义dict类时,我需要了解很多关于dict的信息,因为我需要预测键名并在setitem(第一个示例)或dict-level属性对象(第二个示例)中切换它们。理想情况下,我希望将setter与值对象本身关联起来,这样dict就不需要知道键可以引用的值有什么特别的地方。
python对象已经在dict中存储了属性,那么在这里使用另一个dict有什么意义呢?只需按照通常的方式存储属性(在有意义的地方使用属性),并在需要时动态构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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) |
如果这是一个重新固化模式,您最终可以编写自己的描述符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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']) |
如果使用
如果您想访问一个描述符,访问它的方法是使用类似于此
你只想控制在听写中如何调用键?您可以参考这个问题python:如何"完美地"重写dict来重写dict类中的
我有一个类似的案例,并用描述符来管理它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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') |
现在我们可以:
1 2 3 | p = Person() p.hair = 'purple' |
将打印"不允许"。因为值"紫色"不符合约束。
1 | p.eyes = 1 |
将"Eyes"设置为1。
如前所述:python对象已经在dict中存储了属性,所以您可以用"desc"来代理它。
1 2 3 4 5 6 7 | class SomeClass(object): def __init__(self, d): self.__dict__ = d @property def desc(self): return self.__dict__ |
此外,还可以从此类继承,以更新对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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) |
或者使用命名元组