更改*splat和**splat -splat操作符对对象的操作



如何重写解包语法*obj**obj的结果?

例如,你能不能创建一个对象thing,它的行为是这样的:

>>> [*thing]
['a', 'b', 'c']
>>> [x for x in thing]
['d', 'e', 'f']
>>> {**thing}
{'hello world': 'I am a potato!!'}

注意:通过__iter__的迭代("for x in thing")返回与*splat解包不同的元素。

我看了看operator.muloperator.pow,但这些函数只关注两个操作数的用法,如a*ba**b,似乎与splat操作无关。

*遍历对象并使用其元素作为参数。**迭代对象的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))做不同的事情,那将是非常令人困惑的。在任何情况下,***都是函数调用语法的一部分,而不是单独的操作符,因此自定义它们(如果可能的话)将是可调用对象的工作,而不是参数的工作。我想可能有允许可调用自定义它们的用例,也许通过dict子类如defaultdict

我确实成功地创造了一个符合我在问题中描述的行为的对象,但我真的不得不作弊。所以在这里发布只是为了好玩,真的-

class Thing:
    def __init__(self):
        self.mode = 'abc'
    def __iter__(self):
        if self.mode == 'abc':
            yield 'a'
            yield 'b'
            yield 'c'
            self.mode = 'def'
        else:
            yield 'd'
            yield 'e'
            yield 'f'
            self.mode = 'abc'
    def __getitem__(self, item):
        return 'I am a potato!!'
    def keys(self):
        return ['hello world']

迭代器协议由__iter__返回的生成器对象来满足(注意Thing()实例本身不是迭代器,尽管它是可迭代的)。如果存在keys()__getitem__,则满足映射协议。然而,如果它还不是很明显,你不能在一行中调用*thing两次,并让它在一行中解包a,b,c两次——所以它并没有像它假装的那样真正覆盖splat。

相关内容

  • 没有找到相关文章

最新更新