class 的自定义双星运算符?
Custom double star operator for a class?
如何实现自定义双星运算符 (**
) 来解包,类似于 __iter__
与单星运算符 (*
) 的工作方式?
例如:
class PlayerManager(object):
def __init__(self, players=None):
self.players = players or []
# Made up method to support ** operator
def __dict_iter__(self):
for player in self.players:
yield get_steamid(player), player
def print_players(**players):
print(players)
player_manager = PlayerManager([list, of, players])
print_players(**player_manager)
输出:
{
'STEAM_0:0:02201': <Player object at 0x0000000000>,
'STEAM_0:0:10232': <Player object at 0x0000000064>,
'STEAM_0:0:73602': <Player object at 0x0000000128>
}
实施 Mapping
ABC. Technically, the language docs don't specify which Mapping
methods are used, so assuming you only need some subset used by the current implementation is a bad idea. All it says is:
If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments. In the case of a keyword appearing in both expression and as an explicit keyword argument, a TypeError exception is raised.
所以如果你实现Mapping
ABC,你肯定有正确的接口,不管它是否依赖.items()
,直接迭代和__getitem__
调用等
仅供参考,在检查时,CPython 3.5 中的行为绝对取决于 如何 你实现 Mapping
(如果你继承自 dict
,它使用直接访问 dict
内部的优化路径,如果你不这样做,它会迭代 .keys()
并查找每个键)。所以,是的,不要偷工减料,实施整个 ABC。多亏了从 Mapping
ABC 及其父类继承的默认实现,只需以下代码即可完成此操作:
class MyMapping(Mapping):
def __getitem__(self, key):
...
def __iter__(self):
...
def __len__(self):
...
您继承的默认实现在某些情况下可能不是最优的(例如 items
和 values
会做涉及迭代和查找的半邪恶的事情,其中直接访问器可能更快,具体取决于内部结构),所以如果您将它用于其他目的,我建议使用优化版本覆盖那些。
正如@ShadowRanger 所说,实现映射。这是一个例子:
from collections.abc import Mapping
class Foo(Mapping):
def __iter__(self):
yield "a"
yield "b"
def __len__(self):
return 2
def __getitem__(self, item):
return ord(item)
f = Foo()
print(*f)
print(dict(**f))
程序输出:
a b
{'a': 97, 'b': 98}
如何实现自定义双星运算符 (**
) 来解包,类似于 __iter__
与单星运算符 (*
) 的工作方式?
例如:
class PlayerManager(object):
def __init__(self, players=None):
self.players = players or []
# Made up method to support ** operator
def __dict_iter__(self):
for player in self.players:
yield get_steamid(player), player
def print_players(**players):
print(players)
player_manager = PlayerManager([list, of, players])
print_players(**player_manager)
输出:
{
'STEAM_0:0:02201': <Player object at 0x0000000000>,
'STEAM_0:0:10232': <Player object at 0x0000000064>,
'STEAM_0:0:73602': <Player object at 0x0000000128>
}
实施 Mapping
ABC. Technically, the language docs don't specify which Mapping
methods are used, so assuming you only need some subset used by the current implementation is a bad idea. All it says is:
If the syntax **expression appears in the function call, expression must evaluate to a mapping, the contents of which are treated as additional keyword arguments. In the case of a keyword appearing in both expression and as an explicit keyword argument, a TypeError exception is raised.
所以如果你实现Mapping
ABC,你肯定有正确的接口,不管它是否依赖.items()
,直接迭代和__getitem__
调用等
仅供参考,在检查时,CPython 3.5 中的行为绝对取决于 如何 你实现 Mapping
(如果你继承自 dict
,它使用直接访问 dict
内部的优化路径,如果你不这样做,它会迭代 .keys()
并查找每个键)。所以,是的,不要偷工减料,实施整个 ABC。多亏了从 Mapping
ABC 及其父类继承的默认实现,只需以下代码即可完成此操作:
class MyMapping(Mapping):
def __getitem__(self, key):
...
def __iter__(self):
...
def __len__(self):
...
您继承的默认实现在某些情况下可能不是最优的(例如 items
和 values
会做涉及迭代和查找的半邪恶的事情,其中直接访问器可能更快,具体取决于内部结构),所以如果您将它用于其他目的,我建议使用优化版本覆盖那些。
正如@ShadowRanger 所说,实现映射。这是一个例子:
from collections.abc import Mapping
class Foo(Mapping):
def __iter__(self):
yield "a"
yield "b"
def __len__(self):
return 2
def __getitem__(self, item):
return ord(item)
f = Foo()
print(*f)
print(dict(**f))
程序输出:
a b
{'a': 97, 'b': 98}