如何实现自定义双星运算符 (
**
) 进行解包,类似于 __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>
}
正如@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}
Mapping
ABC。从技术上讲,语言文档没有指定使用哪些 Mapping
方法,因此假设您只需要当前实现使用的一些子集是一个坏主意。 它只说:
如果语法**表达式出现在函数调用中,则表达式必须计算为映射,其内容被视为附加关键字参数。如果关键字同时出现在表达式中并作为显式关键字参数,则会引发 TypeError 异常。
因此,如果你实现了
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
会做涉及迭代和查找的半邪恶的事情,其中直接访问器可能会更快,具体取决于内部结构),所以如果您使用用于其他目的,我建议用优化版本覆盖那些。
您可以使用少于完整的
Mapping
。只需要 .keys()
和 __getitem__
。您可以看到 SupportsKeysAndGetItem
在 typeshed 中被广泛使用。要用作 **kwargs
,键必须是字符串。您可以使用以下协议来静态检查 kwargs 是否可行。
from typing import Protocol, Iterable, Any
class SupportsKwargs(Protocol):
def keys(self) -> Iterable[str]: ...
def __getitem__(self, key: str, /) -> Any: ...
class Foo:
def keys(self) -> list[str]:
return ["some", "strings"]
def __getitem__(self, key) -> int:
return len(key)
class Bar:
def keys(self) -> list[int]:
return [1, 2]
def __getitem__(self, key) -> int:
return key
def unpack_kwargs(**kwargs):
print(kwargs)
def unpack_obj(obj: SupportsKwargs) -> None:
unpack_kwargs(**obj)
unpack_obj(Foo()) # prints {'some': 4, 'strings': 7}
unpack_obj(Bar()) # errors with "TypeError: keywords must be strings"
https://mypy-play.net/?mypy=latest&python=3.11&gist=d4bc1da0e957b2979c3cc7b2dd5fd48f