python:在用户定义的类上应用**运算符

blu*_*ote 2 python overloading parameter-passing kwargs

我创建了一个自定义类,我想**在一个实例上使用运算符将它传递给一个函数.我已经定义__getitem____iter__,但是当我尝试f(**my_object),我得到

`TypeError: argument must be a mapping, not 'MyClass'`
Run Code Online (Sandbox Code Playgroud)

什么是最低要求的方法,以便自定义类有资格作为映射?

Mar*_*ers 5

**它不是运算符,它是调用语法的一部分:

如果语法**expression出现在函数调用中,则表达式必须求值为映射,其内容被视为附加关键字参数.

因此,如果您的类实现了这些Mapping方法,那么您应该很高兴.你需要的不仅仅是__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
Run Code Online (Sandbox Code Playgroud)

如果在调试器下运行它,您将看到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
Run Code Online (Sandbox Code Playgroud)