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

时间:2017-12-04 18:51:25

标签: python parameter-passing overloading kwargs

我创建了一个自定义类,我希望在实例上使用**运算符将其传递给函数。我已经定义了__getitem____iter__,但是当我尝试f(**my_object)时,我正在

`TypeError: argument must be a mapping, not 'MyClass'`

所需的最低方法是什么,以便自定义类符合映射条件?

2 个答案:

答案 0 :(得分:5)

**不是运算符,它是call syntax

的一部分
  

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

因此,如果您的班级实施Mapping methods,那么您应该好好去。您需要的不仅仅是__getitem____iter__

MappingCollection,因此必须至少定义__getitem____iter____len__;此外,大多数__contains__keysitemsvaluesget__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

答案 1 :(得分:1)

Emulating container types

  

第一组方法用于模拟映射......

     

还建议映射提供方法keys()values()items()get()clear()setdefault(),{{ 1}},pop()popitem()copy()的行为与Python标准字典对象的行为类似。

     

建议[...]映射实现update()方法以允许有效使用in运算符......

     

进一步建议[...]映射实现__contains__()方法以允许通过容器进行有效迭代