*
迭代一个对象并将其元素作为参数。 **
迭代一个对象的 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) {
如果
**
参数不是字典,则创建字典并执行普通的
update
以从关键字参数初始化它(除了
PyDict_Update
不接受键值对列表)。因此,您无法单独定制
**
而不实现映射协议。
同样地,对于
*
参数,
ext_do_call
执行。
if (!PyTuple_Check(stararg)) {
PyObject *t = NULL
t = PySequence_Tuple(stararg)
这相当于tuple(args)
。 因此,您无法将*
与普通迭代分开自定义。
如果f(*thing)
和f(*iter(thing))
执行不同的操作,那将会非常令人困惑。无论如何,*
和**
都是函数调用语法的一部分,而不是单独的运算符,因此定制它们(如果可能)将是可调用对象的工作,而不是参数的工作。我想允许可调用对象自定义它们可能有用例,例如通过传递像defaultdict
这样的dict
子类...