*
迭代一个对象并将其元素用作参数。**
遍历一个对象keys
并使用__getitem__
(相当于括号表示法)来获取键值对。要自定义*
,只需使您的对象可迭代,而要自定义**
,请使您的对象成为映射:
class MyIterable(object):
def __iter__(self):
return iter([1, 2, 3])
class MyMapping(collections.Mapping):
def __iter__(self):
return iter('123')
def __getitem__(self, item):
return int(item)
def __len__(self):
return 3
如果你想做除了上面描述*
的**
事情之外的事情,你不能。我没有该声明的文档参考(因为找到“你可以这样做”的文档比“你不能这样做”更容易),但我有一个来源报价。字节码解释器循环PyEval_EvalFrameEx
调用ext_do_call
以实现带有*
或**
参数的函数调用。ext_do_call
包含以下代码:
if (!PyDict_Check(kwdict)) {
PyObject *d;
d = PyDict_New();
if (d == NULL)
goto ext_call_fail;
if (PyDict_Update(d, kwdict) != 0) {
其中,如果**
参数不是 dict,则创建一个 dict 并执行普通update
操作以从关键字参数初始化它(除非它PyDict_Update
不接受键值对列表)。因此,您不能**
与实现映射协议分开定制。
同样,对于*
参数,ext_do_call
执行
if (!PyTuple_Check(stararg)) {
PyObject *t = NULL;
t = PySequence_Tuple(stararg);
这相当于tuple(args)
。因此,您不能*
与普通迭代分开定制。
如果做不同的事情f(*thing)
,那将是非常混乱的。f(*iter(thing))
在任何情况下,*
并且**
是函数调用语法的一部分,而不是单独的运算符,因此自定义它们(如果可能)将是可调用的工作,而不是参数的工作。我想可能存在允许可调用对象自定义它们的用例,也许可以传递dict
子类,例如defaultdict
...