类的自定义双星运算符?

问题描述 投票:0回答:3

如何实现自定义双星运算符 (

**
) 进行解包,类似于
__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>
}
python class python-3.x magic-methods
3个回答
26
投票

正如@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}

17
投票

实施

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
会做涉及迭代和查找的半邪恶的事情,其中直接访问器可能会更快,具体取决于内部结构),所以如果您使用用于其他目的,我建议用优化版本覆盖那些。


0
投票

您可以使用少于完整的

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

© www.soinside.com 2019 - 2024. All rights reserved.