将*splat和**splatty-splat运算符更改为我的对象

wim*_*wim 12 python splat iterable-unpacking double-splat

你如何重写拆包语法的结果*obj**obj

例如,你能以某种方式创建一个thing行为如下的对象:

>>> [*thing]
['a', 'b', 'c']
>>> [x for x in thing]
['d', 'e', 'f']
>>> {**thing}
{'hello world': 'I am a potato!!'}
Run Code Online (Sandbox Code Playgroud)

注意:迭代通过__iter__("for x in thing")返回*splat unpack中的不同元素.

我一看operator.muloperator.pow,但这些功能只关心用途有两个操作数,如a*ba**b,而且似乎无关的图示操作.

use*_*ica 17

*迭代一个对象并使用其元素作为参数.**迭代一个对象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
Run Code Online (Sandbox Code Playgroud)

如果你想要***做一些上述之外的事情,你就不能.我没有该声明的文档参考(因为它更容易找到"你可以做到这一点"的文档而不是"你不能这样做"),但我有一个源引用.字节码解释器循环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) {
Run Code Online (Sandbox Code Playgroud)

如果**参数不是dict,则创建一个dict并执行一个普通的update来从关键字参数初始化它(除了PyDict_Update不接受键值对列表).因此,您无法**单独定制以实现映射协议.

同样,对于*参数,ext_do_call执行

        if (!PyTuple_Check(stararg)) {
            PyObject *t = NULL;
            t = PySequence_Tuple(stararg);
Run Code Online (Sandbox Code Playgroud)

这相当于tuple(args).因此,您无法*与普通迭代分开定制.

这将会是可怕的,如果混乱f(*thing)f(*iter(thing))做不同的事情.在任何情况下,***都是函数调用语法的一部分,而不是单独的运算符,因此自定义它们(如果可能)将是可调用的作业,而不是参数.我想可能有一些用例允许callable自定义它们,也许是为了通过dict子类来传递defaultdict......

  • @wim:文档只是说“这就是 `*` 和 `**` 所做的事情”,而不是“你不能让他们做其他事情”。我想我会去寻找源代码的哪一部分实现了相关的操作码。 (2认同)
  • @mapf:从技术上讲,它仅在当前 CPython 实现中查找“keys”和“__getitem__”,但[语言参考](https://docs.python.org/3/reference/expressions.html#calls)指定解压后的参数必须是映射:“如果函数调用中出现语法‘**表达式’,则‘表达式’必须计算为映射,其内容被视为附加关键字参数。” 实现足以让“**”获取您的对象会导致错误和混乱,因此我故意将其从我的答案中排除。 (2认同)