正确使用字典值的 getter/setter

发布于 2024-12-09 21:19:17 字数 850 浏览 3 评论 0原文

我对 Python 还很陌生,所以如果这里有任何不好的地方,请指出。

我有一个带有这个字典的对象:

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

每个特征的值应该是1-10范围内的整数,并且不允许添加新的特征。我想要 getter/setter,这样我就可以确保保留这些约束。以下是我现在制作 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

我在 此网站上阅读了有关 property() 方法。但我没有看到一种简单的方法来利用它来获取/设置字典中的值。有更好的方法吗?理想情况下,我希望该对象的用法为 obj.traits['happy'] = 14,这将调用我的 setter 方法并抛出 ValueError,因为 14 大于 10。

I'm pretty new to Python, so if there's anything here that's flat-out bad, please point it out.

I have an object with this dictionary:

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

The value for each trait should be an int in the range 1-10, and new traits should not be allowed to be added. I want getter/setters so I can make sure these constraints are being kept. Here's how I made the getter and setter now:

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

I read on this website about the property() method. But I don't see an easy way to make use of it for getting/setting the values inside the dictionary. Is there a better way to do this? Ideally I would like the usage of this object to be obj.traits['happy'] = 14, which would invoke my setter method and throw a ValueError since 14 is over 10.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫烠⑼条掵仅有一顆心 2024-12-16 21:19:17

如果您愿意使用像 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]

If you are willing to use syntax like obj['happy'] = 14 then you could use __getitem__ and __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

If you really do want obj.traits['happy'] = 14 then you could define a subclass of dict and make obj.traits an instance of this subclass.
The subclass would then override __getitem__ and __setitem__ (see below).

PS. To subclass dict, inherit from both collections.MutableMapping, and dict. Otherwise, dict.update would not call the new __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]
耀眼的星火 2024-12-16 21:19:17

我首先想到的是一些明显的提示:

  1. 在检查某个键是否存在时,不要使用 .keys() 方法(而不是 if key not in self.traits.keys() code> 使用如果 key 不在 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

经过上述 我没有彻底检查你的代码的正确性 - 可能还有其他一些问题。

Some obvious tips come to my mind first:

  1. Do not use .keys() method when checking for existence of some key (instead of if key not in self.traits.keys() use if key not in self.traits).
  2. Do not explicitly throw KeyError exception - it is thrown if you try to access inexistent key.

Your code could look like this after above changes:

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

Ps. I did no check the correctness of your code thoroughly - there may be some other issues.

鲸落 2024-12-16 21:19:17

并且不允许添加新的特征。

执行此操作的自然方法是使用对象而不是字典,并设置类的 __slots__

每个特征的值应该是 1-10 范围内的整数...我想要 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.

and new traits should not be allowed to be added.

The natural way to do this is to use an object instead of a dictionary, and set the class' __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.

The natural way to do this is to use an object instead of a dictionary, so that you can write getter/setter logic that's part of the class, and wrap them up as properties. Since all these properties will work the same way, we can do some refactoring to write code that generates a property given an attribute name.

The following is probably over-engineered:

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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文