如何定义可以通过`**`解压缩的自制对象?

时间:2015-12-15 09:28:05

标签: python dictionary mapping

今天我正在学习使用***来解包参数 我发现liststrtupledict都可以*解包。 我猜因为它们都是迭代的。所以我做了自己的课。

# FILE CONTENT
def print_args(*args):
    for i in args:
        print i

class MyIterator(object):
    count = 0
    def __iter__(self):
        while self.count < 5:
            yield self.count
            self.count += 1
        self.count = 0

my_iterator = MyIterator()

# INTERPRETOR TEST
In [1]: print_args(*my_iterator)
0
1
2
3
4

有效!但是如何在python中创建一个mapping对象,如dict,以便**解包对它起作用?有可能吗?除了mapping之外,python中还有另一种dict对象吗?

PS: 我知道我可以使一个对象继承自dict类,使其成为一个映射对象。但有没有像magic_method这样的关键__iter__来制作没有类继承的映射对象?

PS2: 在@ mgilson的回答的帮助下,我创建了一个可以由**解压缩而不从当前映射对象继承的对象:

# FILE CONTENT
def print_kwargs(**kwargs):
    for i, j in kwargs.items():
        print i, '\t', j

class MyMapping(object):
    def __getitem__(self, key):
        if int(key) in range(5):
            return "Mapping and unpacking!"

    def keys(self):
        return map(str, range(5))

my_mapping = MyMapping()
print_kwargs(**my_mapping)

# RESULTS
1   Mapping and unpacking!
0   Mapping and unpacking!
3   Mapping and unpacking!
2   Mapping and unpacking!
4   Mapping and unpacking!

请注意,使用**解压缩时,映射对象中的键应为str,否则将引发TypeError。

2 个答案:

答案 0 :(得分:1)

可以使用任何映射。我建议您继承collections.Mappingcollections.MutableMapping 1 。它们是抽象的基类 - 你提供了几个方法,基类填补了其余的基础。

以下是您可以使用的“冻结”的示例:

from collections import Mapping

class FrozenDict(Mapping):
    """Immutable dictionary.

    Abstract methods required by Mapping are
    1. `__getitem__`
    2. `__iter__`
    3. `__len__`
    """

    def __init__(self, *args, **kwargs):
        self._data = dict(*args, **kwargs)

    def __getitem__(self, key):
        return self._data[key]

    def __iter__(self):
        return iter(self._data)

    def __len__(self):
        return len(self._data)

用法只是:

def printer(**kwargs):
    print(kwargs)

d = FrozenDict({'a': 1, 'b': 2})
printer(**d)

为了回答你关于允许解压缩所需的“魔法”方法的问题 - 仅仅基于实验 - 在Cpython中,__getitem__keys的类足以允许它将被**解压缩。话虽如此,没有保证适用于其他实现(或CPython的未来版本)。要获得保证,您需要实现完整的映射接口(通常在我上面使用的基类的帮助下)。

在python2.x中,还有UserDict.UserDict可以在python3.x中作为collections.UserDict访问 - 但是如果你打算使用这个,你可以经常{{3} }。

1 请注意,从Python3.3开始,这些类已移至collections.abc模块。

答案 1 :(得分:0)

首先,让我们定义解包:

def unpack(**kwargs): 
    """
    Collect all keyword arguments under one hood
    and print them as 'key: value' pairs
    """
    for key_value in kwargs.items(): 
       print('key: %s, value: %s' % key_value)

现在,结构:两个内置选项可用collections.abc.Mappingcollections.UserDict。正如另一个答案探索高度可自定义的Mapping类型一样,我将重点关注UserDictUserDict如果你需要的只是一个基本的dict结构,可以更容易入手一些扭曲。定义后,基础UserDict字典也可以作为.data属性访问。

1.它可以内联使用,如下:

from collections import UserDict
>>> d = UserDict({'key':'value'})

>>> # UserDict makes it feel like it's a regular dict
>>> d, d.data
({'key':'value'}, {'key':'value'})

UserDict分成key = value对:

>>> unpack(**d)
key: key, value: value
>>> unpack(**d.data) # same a above
key: key, value: value

2.如果是子类化,您所要做的就是在__init__中定义self.data。请注意,我使用(self + other)&#39; magic&#39;扩展了该类的附加功能。方法:

class CustomDict(UserDict):
    def __init__(self, dct={}):
        self.data = dct

    def __add__(self, other={}):
        """Returning new object of the same type
           In case of UserDict, unpacking self is the same as unpacking self.data
        """
        return __class__({**self.data, **other})

    def __iadd__(self, other={}):
        """Returning same object, modified in-place"""
        self.update(other)
        return self

用法是:

>>> d = CustomDict({'key': 'value', 'key2': 'value2'})
>>> d
{'key': 'value', 'key2': 'value2'}
>>> type(d), id(d)
(<class '__main__.CustomDict'>, 4323059136)

添加其他字典(或任何mapping类型)将调用__add__,返回新对象:

>>> mixin = {'a': 'aaa', 'b': 'bbb'}
>>> d_new = d + mixin # __add__
>>> d_new
{'key': 'value', 'a': 'aaa', 'key2': 'value2', 'b': 'bbb'} 
>>>type(d_new), id(d_new)
(<class '__main__.CustomDict'>, 4323059248) # new object
>>> d # unmodified
{'key': 'value', 'key2': 'value2'}

使用__iadd__进行就地修改将返回相同的对象(内存中的id相同)

>>> d += {'a': 'aaa', 'b': 'bbb'} # __iadd__
>>> d
{'key': 'value', 'a': 'aaa', 'key2': 'value2', 'b': 'bbb'}
>>> type(d), id(d)
(<class '__main__.CustomDict'>, 4323059136)

顺便说一句,我同意其他贡献者的观点,即你应该熟悉collections.abc.Mapping和兄弟类型。对于基本字典探索,UserDict具有所有相同的功能,并且在变为可用之前不需要覆盖抽象方法。