我对Python很陌生,所以如果这里的任何内容都很糟糕,请指出。
我有一个这个字典的对象:
traits = {'happy': 0, 'worker': 0, 'honest': 0}
每个特征的值应该是1-10范围内的int,并且不应该允许添加新特征。我想要getter / setter所以我可以确保保留这些约束。以下是我现在制作吸气剂和制定者的方法:
def getTrait(self, key):
if key not in self.traits.keys():
raise KeyError
return traits[key]
def setTrait(self, key, value):
if key not in self.traits.keys():
raise KeyError
value = int(value)
if value < 1 or value > 10:
raise ValueError
traits[key] = value
我在this website上了解了property()
方法。但我没有看到一种简单的方法来利用它来获取/设置字典中的值。有一个更好的方法吗?理想情况下,我希望这个对象的使用是obj.traits['happy'] = 14
,它将调用我的setter方法并抛出一个ValueError,因为14超过10。
答案 0 :(得分:9)
如果您愿意使用obj['happy'] = 14
之类的语法,那么您可以使用__getitem__
和__setitem__
:
def __getitem__(self, key):
if key not in self.traits.keys():
raise KeyError
...
return traits[key]
def __setitem__(self, key, value):
if key not in self.traits.keys():
raise KeyError
...
traits[key] = value
如果你确实想要obj.traits['happy'] = 14
,那么你可以定义一个dict
的子类,并使obj.traits
成为这个子类的一个实例。
然后子类将覆盖__getitem__
和__setitem__
(见下文)。
PS。要继承dict
,请继承collections.MutableMapping
和dict
。否则,dict.update
将不会调用新的__setitem__
。
import collections
class TraitsDict(collections.MutableMapping,dict):
def __getitem__(self,key):
return dict.__getitem__(self,key)
def __setitem__(self, key, value):
value = int(value)
if not 1 <= value <= 10:
raise ValueError('{v} not in range [1,10]'.format(v=value))
dict.__setitem__(self,key,value)
def __delitem__(self, key):
dict.__delitem__(self,key)
def __iter__(self):
return dict.__iter__(self)
def __len__(self):
return dict.__len__(self)
def __contains__(self, x):
return dict.__contains__(self,x)
class Person(object):
def __init__(self):
self.traits=TraitsDict({'happy': 0, 'worker': 0, 'honest': 0})
p=Person()
print(p.traits['happy'])
# 0
p.traits['happy']=1
print(p.traits['happy'])
# 1
p.traits['happy']=14
# ValueError: 14 not in range [1,10]
答案 1 :(得分:2)
首先出现了一些明显的提示:
.keys()
使用if key not in self.traits.keys()
)时,请勿使用if key not in self.traits
方法。在上述更改后,您的代码可能如下所示:
def getTrait(self, key):
return traits[key]
def setTrait(self, key, value):
if key not in self.traits:
raise KeyError
value = int(value)
if value < 1 or value > 10:
raise ValueError
traits[key] = value
聚苯乙烯。我没有彻底检查你的代码的正确性 - 可能还有其他一些问题。
答案 2 :(得分:1)
并且不应该允许添加新特征。
执行此操作的自然方法是使用对象而不是字典,并设置类“__slots__
。
每个特征的值应该是1-10范围内的int ...我想要getter / setter所以我可以确保保留这些约束。
执行此操作的自然方法是使用对象而不是字典,以便您可以编写作为类的一部分的getter / setter逻辑,并将它们作为属性包装起来。由于所有这些属性都以相同的方式工作,我们可以进行一些重构来编写生成给定属性名称的属性的代码。
以下可能是过度设计的:
def one_to_ten(attr):
def get(obj): return getattr(obj, attr)
def set(obj, val):
val = int(val)
if not 1 <= val <= 10: raise ValueError
setattr(obj, attr, val)
return property(get, set)
def create_traits_class(*traits):
class Traits(object):
__slots__ = ['_' + trait for trait in traits]
for trait in traits: locals()[trait] = one_to_ten('_' + trait)
def __init__(self, **kwargs):
for k, v in kwargs.items(): setattr(self, k, v)
for trait in traits: assert hasattr(self, trait), "Missing trait in init"
def __repr__(self):
return 'Traits(%s)' % ', '.join(
'%s = %s' % (trait, getattr(self, trait)) for trait in traits
)
return Traits
example_type = create_traits_class('happy', 'worker', 'honest')
example_instance = example_type(happy=3, worker=8, honest=4)
# and you can set the .traits of some other object to example_instance.