Python OrderedDict __setitem__重载

时间:2016-10-01 21:18:42

标签: python dictionary overloading ordereddictionary

我正在构建一个继承OrderedDict的类,其中每个键都返回一个列表。我想重载 setitem ,这样如果密钥不存在,新的赋值会立即将值放入列表中,否则新值将附加到列表中。以下似乎正在起作用:

from collections import OrderedDict

class ListDict(OrderedDict):
    def __init__(self):
        super(ListDict, self).__init__()

    def __setitem__(self, key, value):
        if key in self:
            self[key].append(value)
        else:
            super(ListDict, self).__setitem__(key, [value])

thedict = ListDict()

thedict['a'] = 'first item'
thedict['b'] = 'another first item'
thedict['a'] = 'a second item?'

print thedict

打印:

$ python inheritex.py
ListDict([('a', ['first item', 'a second item?']), ('b', ['another first item'])])

我宁愿让新项目附加'+ =',或者更像是:

,而不是附加赋值运算符'='。
ListDict['key'] = ListDict['key'] + 'value'

怎么会超载呢?除了可以修补添加函数之外,它是否甚至是改变类行为的Pythonic /可读方法,或者这是否可以接受,因为继承的函数(OrderedDict)未受影响?

2 个答案:

答案 0 :(得分:2)

您已经可以在现有键上使用+=

>>> from collections import OrderedDict
>>> thedict = OrderedDict()
>>> thedict['a'] = []  # set initial list
>>> thedict['a'] += ['foo']
>>> thedict['a'] += ['bar']
>>> thedict
OrderedDict([('a', ['foo', 'bar'])])

请注意,列表中的+=list.extend()基本相同,因此您需要附加列表。

如果您希望此功能适用于尚不存在的密钥,请实施__missing__ method,而不是__setitem__

class ListDict(OrderedDict):
    def __missing__(self, key):
        self[key] = []
        return self[key]

如果在dict[key]查询期间缺少某个密钥,则会调用__missing__并返回其返回值,而不是提出KeyError

现在,++=都可以处理丢失的密钥:

>>> thedict = ListDict()
>>> thedict['a'] += ['foo', 'bar']
>>> thedict['b'] = thedict['b'] + ['spam', 'ham']
>>> thedict
ListDict([('a', ['foo', 'bar']), ('b', ['spam', 'ham'])])

如果连接必须在不添加列表的情况下工作,您也可以生成自定义列表子类:

class ConcatList(list):
    def __add__(self, value):
        return type(self)(super(ContactList, self).__add__([value]))
    def __iadd__(self, value):
        self.append(value)
        return self

然后在__missing__中使用该类型(并转换直接在__setitem__中设置的任何新列表):

class ListDict(OrderedDict):
    def __missing__(self, key):
        self[key] = ConcatList()
        return self[key]
    def __setitem__(self, key, value):
        if not isinstance(key, ConcatList):
           value = ConcatList([value])
        super(ListDict, self).__setitem__(key, value)

之后你可以放弃括号:

>>> thedict = ListDict()
>>> thedict['a'] += 'foo'
>>> thedict['b'] = thedict['b'] + 'bar'
>>> thedict
ListDict([('a', ['foo']), ('b', ['bar'])])

答案 1 :(得分:0)

我已延长Martijn's ListDict,因此您可以提供default_factory并具有类似于collections.defaultdict的功能+密钥排序:

class OrderedDefaultDict(OrderedDict):
    def __init__(self, default_factory=None):
        self.default_factory = default_factory

    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError
        self[key] = self.default_factory()
        return self[key]

list_dict = OrderedDefaultDict(list)
list_dict['first'].append(1)
list_dict['second'].append(2)
list_dict['third'].append(3)
assert list(list_dict.keys()) == ['first', 'second', 'third']
assert list(list_dict.values()) == [[1], [2], [3]]

list_dict.move_to_end('first')
assert list(list_dict.keys()) == ['second', 'third', 'first']