如何在Python 3中正确包装dict?

时间:2018-06-13 15:37:14

标签: python python-3.x dictionary

有时候能够像对象一样使用字典会很好(所以你不必总是写mydict['blah']。相反,你可以写mydict.blah < / p>

在Python 3中,有关于覆盖/包装python字典的新规则。在Python2中,它足以包装__getattr____setattr__方法。当你包装这些东西时,你会得到一些很好的功能,可以在添加某个属性时添加一些特殊处理(例如,你可以清理/过滤dict中的数据)。

这有用的情况是在Flask模板(HTML模板)中。使用__getattr__过滤器,您可以在数据离开dict时格式化数据。这样,在模板中(Python表达式看起来有点复杂),只需编写mymodel.blah并且知道来自blah的文本已经是您想要的方式,就可以使事情变得简单它

在Python3中包装dict有点乱。我现在还不确定如何让它发挥作用。以下是两个不能正常运行的粗略实现:

# messed up Python3 wrapped dictionary (sets work, but gets do not)
class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self
        self.clean_strings()
    def clean_strings(self):
        for key, value in self.items():
            self[key] = string_empty_to_none(value)
    def __getattr__(self, name):
        #this never gets called in Python 3
        return self[name]

这是另一个:

# sets don't work - Always throws: TypeError: 'AttrDict' object does not support item assignment
class AttrDict():
    def __init__(self, *args, **kwargs):
        self.data = dict()
        self.data.__init__(*args, **kwargs)
        self.clean_strings()
    def clean_strings(self):
        for key, value in self.data.items():
            self.data[key] = string_empty_to_none(value)
    def __getattr__(self, attr):
        if attr in self.data:
            return self.data[attr]
        else:
            raise AttributeError("--%r object has no attribute %r" % (type(self).__name__, attr)) 
    def __setattr__(self, name, value):
        if name == 'data':
            super(AttrDict, self).__setattr__(name, value)
        else:
            self.data[name] = value

这是我的小实用工具的样子:

def string_empty_to_none(s):
    if type(s) == str:
            return None if (s.strip() == '') else s

    return s

我知道在Python3中,你应该使用__getattribute__而不是__getattr__但是当我这样做时,它似乎总是在无限循环中结束。

注意:我正在寻找的最终语法是这样的:

>>> x = AttrDict({'some': 'value'})
>>> x['blah'] = 'hello world'
>>> print(x.blah)
hello world
>>> print(x.some)
value

3 个答案:

答案 0 :(得分:1)

实际上,不需要在self.__dict__继承类的__init__方法中进行dict分配 - 只需执行通常的arg传递给super(),然后你的处理。实际上我甚至不确定self.__dict__ = self意味着什么 - 我可以想象,如果你压倒他们的__dict__(即使他们的self es&#34,它会打破一些内部行为。 #34;。)

最好修改__getattr__方法,因为如果__getattribute__找不到任何内容,这将是Python的回退方法。另一方面,如果您知道自己大部分时间都在使用属性样式的访问者,那么您可以切换该逻辑。

请参阅以下示例:

def string_empty_to_none(s):
  if type(s) == str:
    return None if (s.strip() == '') else s
  return s

# This will always have issues, even in IDE's
print('AttrDict')
class AttrDict(dict):
  def __init__(self, *args, **kwargs):
    super(AttrDict, self).__init__(*args, **kwargs)
    self.__dict__ = self
    self.clean_strings()
  def clean_strings(self):
    for key, value in self.items():
      self[key] = string_empty_to_none(value)

test = AttrDict({'a': 1})
test['x'] = 2
test.z = ""
print(test.a)
print(test['a'])
print(test.x)
print(test['x'])
print(test.z)
print(test['z'])


print('MostlyPropertiesAccessDict')
class MostlyPropertiesAccessDict(dict):
  def __init__(self, *args, **kwargs):
    # No need for the self.__dict__ part
    super().__init__(*args, **kwargs)
    self.clean_strings()

  def clean_strings(self):
    for key, value in self.items():
      self[key] = string_empty_to_none(value)

  def __getattr__(self, name):
    if not name in self:
      raise AttributeError(
        "Attribute {} does not exist".format(name))
    return self[name]

  def __setattr__(self, name, value):
    self[name] = string_empty_to_none(value)

  def __delattr__(self, name):
    if not name in self:
      raise AttributeError(
        "Attribute {} does not exist".format(name))
    del self[name]

test2 = MostlyPropertiesAccessDict({'a': 1})
test2['x'] = 2
test2.z = ""
print(test2.a)
print(test2['a'])
print(test2.x)
print(test2['x'])
print(test2.z)
print(test2['z'])

print("MostlyKeysAccessDict")
class MostlyKeysAccessDict(dict):
  def __init__(self, *args, **kwargs):
    # No need for the self.__dict__ part
    super().__init__(*args, **kwargs)
    self.clean_strings()

  def clean_strings(self):
    for key, value in self.items():
      self[key] = string_empty_to_none(value)

  def __getattribute__(self, name):
    if not name in self:
      raise AttributeError(
        "Attribute {} does not exist".format(name))
    return self[name]

  def __getattr__(self, name):
    return super().__getattribute__(name)

  def __setattr__(self, name, value):
    self[name] = string_empty_to_none(value)

  def __delattr__(self, name):
    if not name in self:
      raise AttributeError(
        "Attribute {} does not exist".format(name))
    del self[name]

test3 = MostlyKeysAccessDict({'a': 1})
test3['x'] = 2
test3.z = ""
print(test3.a)
print(test3['a'])
print(test3.x)
print(test3['x'])
print(test3.z)
print(test3['z'])

答案 1 :(得分:0)

您可以使用__getitem__内的__getattr__覆盖来实现此目的。

class AttrDict(dict):

    def __getattr__(self, item):
        return super().__getitem__(item)

    def __setattr__(self, item, value):
        return super().__setitem__(item, value)


x = AttrDict({'some': 'value'})
x['blah'] = 'hello world'
print(x.blah)  # hello world
print(x.some)  # value

# you can also assign value this way
x.foo = 'bar'
print(x['foo'])   # bar

答案 2 :(得分:0)

您需要做的就是创建一个包含字典的包装类,然后实现__getitem__

class DictionaryWrapper():

    _dict = {"key1": "value1", "key2": "value2"}

    def __getitem__(self, item):
        return self._dict[item]

    @attribute
    def key1(self):
        return self._dict["key1"]

现在,您可以将DictionartWrapper视为字典。 (此受限的实现方式仅允许阅读。)

my_dictionary = DictionaryWrapper()
print(my_dictionary["key1"])

如果字典中的键是已知的,您甚至可以通过包装类上的属性公开它们的值。

print(my_dictionary.key1)

有关类似的讨论,请参见here