一个班级的定制双星操作员?

时间:2015-10-28 21:49:49

标签: python class python-3.x magic-methods

如何实现自定义双星运算符(**)进行解包,类似于__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>
}

2 个答案:

答案 0 :(得分:14)

正如@ShadowRanger所说,实现Mapping。这是一个例子:

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}

答案 1 :(得分:9)

实施Mapping ABC。从技术上讲,语言文档没有指定使用哪些Mapping方法,因此假设您只需要当前实现使用的某个子集是一个坏主意。 All it says is

  

如果语法**表达式出现在函数调用中,则表达式必须求值为映射,其内容被视为附加关键字参数。如果关键字出现在表达式和显式关键字参数中,则会引发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):
        ...

在某些情况下,您继承的默认实现可能不是最理想的(例如itemsvalues会涉及迭代和查找的半邪恶内容,其中直接访问器可能会更快,具体取决于内部结构),因此,如果您将其用于其他目的,我建议优先使用优化版本。