*迭代一个对象并将其元素用作参数。**遍历一个对象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...