** 不是运算符,它是call syntax 的一部分:
如果语法**expression出现在函数调用中,则表达式必须计算为mapping,其内容被视为附加关键字参数。
因此,如果您的班级实现了Mapping methods,那么您应该很高兴。您需要的不仅仅是__getitem__ 和__iter__。
Mapping 是Collection,因此必须至少定义__getitem__、__iter__ 和__len__;此外,大多数__contains__、keys、items、values、get、__eq__ 和__ne__ 都是预期的。如果你的自定义类直接继承自collections.abc.Mapping,只需要实现前三个即可。
演示:
>>> from collections.abc import Mapping
>>> class DemoMapping(Mapping):
... def __init__(self, a=None, b=None, c=None):
... self.a, self.b, self.c = a, b, c
... def __len__(self): return 3
... def __getitem__(self, name): return vars(self)[name]
... def __iter__(self): return iter('abc')
...
>>> def foo(a, b, c):
... print(a, b, c)
...
>>> foo(**DemoMapping(42, 'spam', 'eggs'))
42 spam eggs
如果您在调试器下运行它,您会看到 Python 调用 .keys() 方法,该方法返回一个字典视图,然后在迭代视图时委托给自定义类 __iter__ 方法。然后通过一系列__getitem__ 调用检索这些值。因此,对于您的具体情况,缺少的是 .keys() 方法。
另外,请注意 Python 可能会强制要求键是字符串!
>>> class Numeric(Mapping):
... def __getitem__(self, name): return {1: 42, 7: 'spam', 11: 'eggs'}[name]
... def __len__(self): return 3
... def __iter__(self): return iter((1, 7, 11))
...
>>> dict(Numeric())
{1: 42, 7: 'spam', 11: 'eggs'}
>>> def foo(**kwargs): print(kwargs)
...
>>> foo(**Numeric())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings