【问题标题】:Custom double star operator for a class?类的自定义双星运算符?
【发布时间】:2016-01-28 21:15:30
【问题描述】:

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


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

      如果语法 **expression 出现在函数调用中,则 expression 必须计算为一个映射,其内容被视为附加关键字参数。如果关键字同时出现在表达式中并作为显式关键字参数出现,则会引发 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 会做涉及迭代和查找的半坏事,其中直接访问器可能会更快,具体取决于内部结构),所以如果你将它用于其他目的,我建议用优化版本覆盖那些。

      【讨论】:

      • 非常感谢,我接受了@codeape 的回答,因为它清楚地表明我需要做些什么才能让我的班级支持**,但我确实给你投了赞成票!
      • @MarkusMeskanen:奇怪。当您写这篇文章时,我正在详细说明,但 SO 没有通知我(我仍然没有对此的评论提醒,并且代表在必须投票后几分钟没有出现)。嗯。随便。
      • @ShadowRanger:碰巧知道 CPython 的哪些部分实现了这一点?希望在 C 端类中添加对 ** 的支持
      • 啊,found it
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-15
      • 1970-01-01
      • 2012-08-06
      • 2016-01-21
      • 2015-04-10
      • 2019-01-04
      • 2021-06-11
      相关资源
      最近更新 更多