如何定义可以用`**`解包的自制对象?
How to define self-made object that can be unpacked by `**`?
今天我正在学习使用 *
和 **
来解压参数。
我发现 list
, str
, tuple
, dict
都可以被 *
.
解包
我猜是因为它们都是可迭代的。所以我自己做了 class.
# 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 中创建一个像 dict
这样的 mapping
对象,以便 **
对其进行解包?有可能这样做吗?除了 dict
之外,python 中是否已经存在另一种 mapping
对象?
PS:
我知道我可以让一个对象继承自 dict
class 以使其成为一个映射对象。但是是否有像 __iter__
这样的键 magic_method
来创建没有 class 继承的映射对象?
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。
首先,让我们定义解包:
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.Mapping and collections.UserDict。由于还有另一个探索高度可定制的 Mapping
类型的答案,我将专注于 UserDict
:如果您只需要一个带有一些扭曲的基本字典结构,那么 UserDict
可以更容易开始。定义后,底层 UserDict
字典也可以作为 .data
属性访问。
1.It 可以内联使用,像这样:
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
分成键=值对:
>>> unpack(**d)
key: key, value: value
>>> unpack(**d.data) # same a above
key: key, value: value
2.If子classing,你所要做的就是在__init__
中定义self.data。请注意,我使用 (self+other) 'magic' 方法扩展了 class 的附加功能:
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__
,returning 新对象:
>>> 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__
的就地修改将 return 相同的对象(内存中的相同 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 具有所有相同的功能,并且不需要您在可用之前重写抽象方法。
可以使用任何映射。我建议您从 collections.Mapping
或 collections.MutableMapping
1 继承。它们是抽象基础 classes -- 你提供几个方法,基础 class 填充其余部分。
这是您可以使用的 "frozendict" 示例:
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)
回答你的问题,关于哪些 "magic" 方法是允许解包所必需的——仅基于实验——在 Cpython 中 class 和 __getitem__
keys
足以让它用 **
解包。话虽如此,没有 guarantee 适用于其他实现(或 CPython 的未来版本)。要获得保证,您需要实现完整的映射接口(通常在我上面使用的基础 class 的帮助下)。
在python2.x中还有UserDict.UserDict
which can be accessed in python3.x as collections.UserDict
-- However if you're going to use this one, you can frequently just subclass from dict
.
1请注意,从 Python3.3 开始,那些 classes 已移至 collections.abc
模块.
今天我正在学习使用 *
和 **
来解压参数。
我发现 list
, str
, tuple
, dict
都可以被 *
.
解包
我猜是因为它们都是可迭代的。所以我自己做了 class.
# 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 中创建一个像 dict
这样的 mapping
对象,以便 **
对其进行解包?有可能这样做吗?除了 dict
之外,python 中是否已经存在另一种 mapping
对象?
PS:
我知道我可以让一个对象继承自 dict
class 以使其成为一个映射对象。但是是否有像 __iter__
这样的键 magic_method
来创建没有 class 继承的映射对象?
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。
首先,让我们定义解包:
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.Mapping and collections.UserDict。由于还有另一个探索高度可定制的 Mapping
类型的答案,我将专注于 UserDict
:如果您只需要一个带有一些扭曲的基本字典结构,那么 UserDict
可以更容易开始。定义后,底层 UserDict
字典也可以作为 .data
属性访问。
1.It 可以内联使用,像这样:
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
分成键=值对:
>>> unpack(**d)
key: key, value: value
>>> unpack(**d.data) # same a above
key: key, value: value
2.If子classing,你所要做的就是在__init__
中定义self.data。请注意,我使用 (self+other) 'magic' 方法扩展了 class 的附加功能:
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__
,returning 新对象:
>>> 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__
的就地修改将 return 相同的对象(内存中的相同 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 具有所有相同的功能,并且不需要您在可用之前重写抽象方法。
可以使用任何映射。我建议您从 collections.Mapping
或 collections.MutableMapping
1 继承。它们是抽象基础 classes -- 你提供几个方法,基础 class 填充其余部分。
这是您可以使用的 "frozendict" 示例:
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)
回答你的问题,关于哪些 "magic" 方法是允许解包所必需的——仅基于实验——在 Cpython 中 class 和 __getitem__
keys
足以让它用 **
解包。话虽如此,没有 guarantee 适用于其他实现(或 CPython 的未来版本)。要获得保证,您需要实现完整的映射接口(通常在我上面使用的基础 class 的帮助下)。
在python2.x中还有UserDict.UserDict
which can be accessed in python3.x as collections.UserDict
-- However if you're going to use this one, you can frequently just subclass from dict
.
1请注意,从 Python3.3 开始,那些 classes 已移至 collections.abc
模块.