参数解压缩是否使用迭代或项获取?

伊利

我正在使用Python 2.7.3。

考虑一个具有自定义(尽管较差)迭代和获取项行为的伪类:

class FooList(list):
    def __iter__(self):
        return iter(self)
    def next(self):
        return 3
    def __getitem__(self, idx):
        return 3

举个例子,看看奇怪的行为:

>>> zz = FooList([1,2,3])

>>> [x for x in zz]
# Hangs because of the self-reference in `__iter__`.

>>> zz[0]
3

>>> zz[1]
3

但是现在,让我们创建一个函数,然后对以下参数进行解压缩zz

def add3(a, b, c):
    return a + b + c

>>> add3(*zz)
6
# I expected either 9 or for the interpreter to hang like the comprehension!

因此,参数解压缩以某种方式获取项目数据,zz但不是通过使用实现的迭代器遍历对象,也不是通过穷人的迭代器并调用__getitem__与对象一样多的项目来获取项目数据。

所以问题是:如果不通过这些方法,语法如何add3(*zz)获取其数据成员zz我是否只是想从这样的类型中获取数据成员而错过另一种常见模式?

我的目标是看我是否可以编写实现迭代或项获取的类,以使其更改参数解包语法对该类的含义。在尝试了上面的两个示例之后,我现在想知道参数解包如何到达底层数据以及程序员是否可以影响该行为。谷歌为此只给出了解释*args语法基本用法的大量结果

我没有用例需要这样做,我并不是说这是个好主意。为了好奇,我只想看看该怎么做。

添加

由于内置类型是经过特殊处理的,因此这里有一个示例,object其中我只维护一个列表对象并实现自己的get和set行为以模拟列表。

class FooList(object):
    def __init__(self, lst):
        self.lst = lst
    def __iter__(self): raise ValueError
    def next(self): return 3
    def __getitem__(self, idx): return self.lst.__getitem__(idx)
    def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

在这种情况下,

In [234]: zz = FooList([1,2,3])

In [235]: [x for x in zz]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-235-ad3bb7659c84> in <module>()
----> 1 [x for x in zz]

<ipython-input-233-dc9284300db1> in __iter__(self)
      2     def __init__(self, lst):
      3         self.lst = lst
----> 4     def __iter__(self): raise ValueError
      5     def next(self): return 3
      6     def __getitem__(self, idx): return self.lst.__getitem__(idx)

ValueError:

In [236]: add_3(*zz)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-236-f9bbfdc2de5c> in <module>()
----> 1 add_3(*zz)

<ipython-input-233-dc9284300db1> in __iter__(self)
      2     def __init__(self, lst):
      3         self.lst = lst
----> 4     def __iter__(self): raise ValueError
      5     def next(self): return 3
      6     def __getitem__(self, idx): return self.lst.__getitem__(idx)

ValueError:

但是,相反,如果我确保迭代停止并且总是返回3,那么我可以得到在第一种情况下我要拍摄的内容:

class FooList(object):
    def __init__(self, lst):
        self.lst = lst
        self.iter_loc = -1
    def __iter__(self): return self
    def next(self): 
        if self.iter_loc < len(self.lst)-1:
            self.iter_loc += 1
            return 3
        else:
            self.iter_loc = -1
            raise StopIteration
    def __getitem__(self, idx): return self.lst.__getitem__(idx)
    def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

然后我看到了,这是我最初的期望:

In [247]: zz = FooList([1,2,3])

In [248]: ix = iter(zz)

In [249]: ix.next()
Out[249]: 3

In [250]: ix.next()
Out[250]: 3

In [251]: ix.next()
Out[251]: 3

In [252]: ix.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-252-29d4ae900c28> in <module>()
----> 1 ix.next()

<ipython-input-246-5479fdc9217b> in next(self)
     10         else:
     11             self.iter_loc = -1
---> 12             raise StopIteration
     13     def __getitem__(self, idx): return self.lst.__getitem__(idx)
     14     def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

StopIteration:

In [253]: ix = iter(zz)

In [254]: ix.next()
Out[254]: 3

In [255]: ix.next()
Out[255]: 3

In [256]: ix.next()
Out[256]: 3

In [257]: ix.next()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-257-29d4ae900c28> in <module>()
----> 1 ix.next()

<ipython-input-246-5479fdc9217b> in next(self)
     10         else:
     11             self.iter_loc = -1
---> 12             raise StopIteration
     13     def __getitem__(self, idx): return self.lst.__getitem__(idx)
     14     def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)

StopIteration:

In [258]: add_3(*zz)
Out[258]: 9

In [259]: zz[0]
Out[259]: 1

In [260]: zz[1]
Out[260]: 2

In [261]: zz[2]
Out[261]: 3

In [262]: [x for x in zz]
Out[262]: [3, 3, 3]

概要

  1. 语法*args仅依赖迭代。对于内置类型,这种情况在从内置类型继承的类中无法直接覆盖。

  2. 这两个在功能上是等效的:

    foo(*[x for x in args])

    foo(*args)

  3. 即使对于有限的数据结构,这些也不相等。

    foo(*args)

    foo(*[args[i] for i in range(len(args))])

布伦·巴恩

您被Python最令人讨厌的疣之一所bit:内置类型和它们的子类在某些地方被神奇地对待。

由于您的类型是的子类list,因此Python会神奇地进入其内部以对其进行解压缩。它根本不使用真正的迭代器API。如果printnext和中插入语句__getitem__,则会看到两个都没有被调用。此行为不能被覆盖。相反,您将必须编写自己的类以重新实现内置类型。您可以尝试使用UserList我还没有检查是否可行。

您问题的答案是,参数解压缩使用迭代。但是,__getitem__如果没有显式__iter__定义,则可以使用迭代本身您不能创建一个定义与常规迭代行为不同的参数解压缩行为的类。

__iter__不应假定迭代器协议(基本上是“如何工作”)适用于子类内置类型的类型,例如list如果您将内建子类化,则子类在某些情况下可能会像基础内建子一样神奇地表现,而无需使用自定义魔术方法(例如__iter__)。如果您想完全可靠地自定义行为,则不能从内置类型中继承子类(当然,除外object)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

从argparse解压缩参数

来自分类Dev

解压缩源参数

来自分类Dev

使用数组(或initializer_list)技巧解压缩可变参数是否安全?

来自分类Dev

是否可以在解压缩列表之后使用类型名作为模板参数?

来自分类Dev

使用工件名称解压缩gradle依赖项

来自分类Dev

使用工件名称解压缩gradle依赖项

来自分类Dev

使用Lambda解压缩可变参数时的增量指针

来自分类Dev

如何解压缩迭代器?

来自分类Dev

从模板参数解压缩参数包

来自分类Dev

使用 jq 解压/解压缩 JSON

来自分类Dev

lambda参数解压缩错误

来自分类Dev

std :: tuple解压缩多个参数

来自分类Dev

尝试获取javadoc报告时,Maven站点在解压缩依赖项上失败

来自分类Dev

使用NTFS压缩的文件是否已解压缩到磁盘或内存中?

来自分类Dev

Extendscript 是否能够使用文件对象压缩/解压缩文件?

来自分类Dev

Python:是否有语法级别的支持从元组将参数解压缩到* anonymous *函数?

来自分类Dev

是否有Fortran的等效功能,可以解压缩Python中的参数列表?

来自分类Dev

是否可以在JuliaDB push!()函数中解压缩参数字典?

来自分类Dev

Python:是否可以在不调用函数的情况下解压缩参数?

来自分类Dev

Python:是否有语法级别的支持从元组将参数解压缩到* anonymous *函数?

来自分类Dev

使用python解压缩大文件

来自分类Dev

使用DeflateStream解压缩字节

来自分类Dev

使用GzipInputStream解压缩为byte []

来自分类Dev

使用Swift解压缩zip文件

来自分类Dev

使用Lambda解压缩变量

来自分类Dev

使用python解压缩大文件

来自分类Dev

使用通配符解压缩特定文件

来自分类Dev

无法使用gunzip解压缩.tgz

来自分类Dev

使用VBS解压缩时出错

Related 相关文章

热门标签

归档