对字典值正确使用getter/setter

2024-06-13 17:04:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我对Python还不太熟悉,所以如果这里有什么不好的地方,请指出。

我对这本词典有一个看法:

traits = {'happy': 0, 'worker': 0, 'honest': 0}

每个性状的值应该是1-10范围内的整数,并且不允许添加新性状。我想要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。


Tags: 方法keyinselfifvaluedefnot
3条回答

and new traits should not be allowed to be added.

这样做的自然方法是使用对象而不是字典,并设置类“__slots__”。

The value for each trait should be an int in the range 1-10... I want getter/setters so I can make sure these constraints are being kept.

这样做的自然方法是使用对象而不是字典,这样您就可以编写属于类的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.

我首先想到了一些显而易见的建议:

  1. 检查某个键是否存在时不要使用.keys()方法(而不是if key not in self.traits.keys()使用if key not in self.traits)。
  2. 不要显式抛出KeyError异常-如果试图访问不存在的密钥,则会抛出该异常。

经过上述更改后,您的代码可能如下所示:

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

我没有彻底检查你的代码的正确性-可能还有其他问题。

如果您愿意使用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__(见下文)。

另一方面,子类dict,继承自collections.MutableMappingdict。否则,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]

相关问题 更多 >