参数是否拆包使用迭代或得到项目,? [英] Does argument unpacking use iteration or item-getting?

查看:117
本文介绍了参数是否拆包使用迭代或得到项目,?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Python 2.7.3。

考虑使用自定义的虚拟类(虽然坏)迭代和获取项目行为:

 类FooList(列表):
    高清__iter __(个体经营):
        返回ITER(个体经营)
    接下来的高清(个体经营):
        返回3
    高清__getitem __(自我,IDX):
        返回3

请一个例子,看到了怪异的行为:

 >>> ZZ = FooList([1,2,3])>>> [X为ZZ X]
#挂起,因为在`__iter__`自我引用。>>> ZZ [0]
3>>> ZZ [1]
3

但现在,让我们做一个函数,然后做论证拆包上 ZZ

  DEF ADD3(A,B,C):
    返回+ B + C>>> ADD3(* ZZ)
6
#我预计9或为国米preTER挂像COM prehension!

所以,参数解包以某种方式从 ZZ 获取项目数据,但不能由任何遍历对象,其实现的迭代器,也不会通过做一个穷人的iterator和调用 __ __的GetItem 为尽可能多的项目为对象了。

所以,问题是:如何做语法 ADD3(* ZZ)如果获得 ZZ 的数据成员没有这些方法?我只是缺少一个常见的​​模式来获取数据的成员从类型这样吗?

我的目标是,看看我是否可以写一个实现类迭代或以这样一种方式,它改变了什么拆包语法的说法意味着该类项目获得 - 。尝试上述两个例子后,我现在想知道的说法拆包如何在获取基础数据和是否程序员可以影响该行为。谷歌为此只给了返回结果的解释 * ARGS 语法的基本用法的海洋。

我没有用例需要做到这一点,我不是说这是一个好主意。我只是想看看如何做到这一点的好奇着想。

添加

由于内建类型被特殊处理,在这里与对象的例子,我只是保持一个列表对象,并实现自己获取和设置行为模仿名单。

 类FooList(对象):
    高清__init __(自我,LST):
        self.lst = LST
    高清__iter __(个体经营):提高ValueError错误
    接下来的高清(个体经营):3回
    高清__getitem __(自我,IDX):返回self.lst .__的GetItem __(IDX)
    高清__setitem __(自我,IDX,ITM):self.lst .__ setitem __(IDX,ITM)

在这种情况下,

 在[234]:ZZ = FooList([1,2,3])在[235]:[X为ZZ X]
-------------------------------------------------- -------------------------
ValueError错误回溯(最新最后调用)
< IPython的输入-235-ad3bb7659c84>上述<模块>()
----> 1 [X为ZZ X]< IPython的输入-233-dc9284300db1>在__iter __(个体经营)
      2高清__init __(自我,LST):
      3 self.lst = LST
----> 4高清__iter __(个体经营):提高ValueError错误
      5在下高清(个体经营):3回
      6 DEF __getitem __(自我,IDX):返回self.lst .__的GetItem __(IDX)ValueError错误:在[236]:ADD_3(* ZZ)
-------------------------------------------------- -------------------------
ValueError错误回溯(最新最后调用)
< IPython的输入-236-f9bbfdc2de5c>上述<模块>()
----> 1 ADD_3(* ZZ)< IPython的输入-233-dc9284300db1>在__iter __(个体经营)
      2高清__init __(自我,LST):
      3 self.lst = LST
----> 4高清__iter __(个体经营):提高ValueError错误
      5在下高清(个体经营):3回
      6 DEF __getitem __(自我,IDX):返回self.lst .__的GetItem __(IDX)ValueError错误:

但是相反,如果我保证迭代停止并且总是返回3,我可以得到什么我拍摄的第一例玩弄:

 类FooList(对象):
    高清__init __(自我,LST):
        self.lst = LST
        self.iter_loc = -1
    高清__iter __(个体经营):自回归
    接下来的高清(个体经营):
        如果self.iter_loc< LEN(self.lst)-1:
            self.iter_loc + = 1
            返回3
        其他:
            self.iter_loc = -1
            抛出StopIteration异常
    高清__getitem __(自我,IDX):返回self.lst .__的GetItem __(IDX)
    高清__setitem __(自我,IDX,ITM):self.lst .__ setitem __(IDX,ITM)

然后我看到这一点,这是我最初的预期:

 在[247]:ZZ = FooList([1,2,3])在[248]:九= ITER(ZZ)在[249]:ix.next()
出[249]:3在[250]:ix.next()
出[250]:3在[251]:ix.next()
出[251]:3在[252]:ix.next()
-------------------------------------------------- -------------------------
StopIteration异常回溯(最新最后调用)
< IPython的输入-252-29d4ae900c28>上述<模块>()
----> 1 ix.next()< IPython的输入-246-5479fdc9217b>在明年(个体经营)
     10其他:
     11 self.iter_loc = -1
---> 12抛出StopIteration异常
     13 DEF __getitem __(自我,IDX):返回self.lst .__的GetItem __(IDX)
     14 DEF __setitem __(自我,IDX,ITM):self.lst .__ setitem __(IDX,ITM)StopIteration异常:在[253]:九= ITER(ZZ)在[254]:ix.next()
出[254]:3在[255]:ix.next()
出[255]:3在[256]:ix.next()
出[256]:3在[257]:ix.next()
-------------------------------------------------- -------------------------
StopIteration异常回溯(最新最后调用)
< IPython的输入-257-29d4ae900c28>上述<模块>()
----> 1 ix.next()< IPython的输入-246-5479fdc9217b>在明年(个体经营)
     10其他:
     11 self.iter_loc = -1
---> 12抛出StopIteration异常
     13 DEF __getitem __(自我,IDX):返回self.lst .__的GetItem __(IDX)
     14 DEF __setitem __(自我,IDX,ITM):self.lst .__ setitem __(IDX,ITM)StopIteration异常:在[258]:ADD_3(* ZZ)
出[258]:9在[259]:ZZ [0]
出[259]:1在[260]:ZZ [1]
出[260]:2在[261]:ZZ [2]
出[261]:3在[262]:[X为ZZ X]
出[262]:[3,3,3]

摘要


  1. 语法 * ARGS 仅依赖于迭代。对于内置类型发生这种情况的方式,不是在从内装型

  2. 继承类直​​接覆写投放
  3. 这是两个功能相同:

    美孚(* [x对于在args X])

    美孚(*参数)


  4. 这些都不是即使有限的数据结构相同的。

    美孚(*参数)

    美孚(* [参数[I]为我的range(LEN(参数)))



解决方案

您已经通过Python的最恼人的疣之一咬伤:内建类型以及它们的子类都奇迹般地对待,有些地方

由于从列表你的类型的子类,Python的奇迹般地达到进入它的内部把它解压。它不使用实迭代器API的。如果您插入在打印语句的接下来 __的GetItem __ ,你会看到,没有一个被调用。这种行为不能被重写;相反,你必须编写自己的类,重新实现了内置的类型。你可以尝试使用 的UserList ;我没有检查这是否会工作。

在回答你的问题是这样的说法拆包使用迭代。然而,迭代本身可以使用 __ __的GetItem 如果没有明确定义的 __ __ ITER 。你不能让一个类定义的说法,拆包行为,它是从正常的迭代行为不同。

迭代器协议(基本上是如何 __ ITER __ 作品)不应该被假定为适用于该子类内置类型,如列表。如果你继承一个内建,你的子类可以神奇地表现得像在某些情况下的底层内置,无需利用你的定制魔术方法(如 __ __ ITER )。如果你想完全可靠的定制行为,你不能从内置类型的子类(除,当然,对象)。

I'm using Python 2.7.3.

Consider a dummy class with custom (albeit bad) iteration and item-getting behavior:

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

Make an example and see the weird behavior:

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

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

>>> zz[0]
3

>>> zz[1]
3

But now, let's make a function and then do argument unpacking on 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!

So, argument unpacking is somehow getting the item data from zz but not by either iterating over the object with its implemented iterator and also not by doing a poor man's iterator and calling __getitem__ for as many items as the object has.

So the question is: how does the syntax add3(*zz) acquire the data members of zz if not by these methods? Am I just missing one other common pattern for getting data members from a type like this?

My goal is to see if I could write a class that implements iteration or item-getting in such a way that it changes what the argument unpacking syntax means for that class. After trying the two example above, I'm now wondering how argument unpacking gets at the underlying data and whether the programmer can influence that behavior. Google for this only gave back a sea of results explaining the basic usage of the *args syntax.

I don't have a use case for needing to do this and I am not claiming it is a good idea. I just want to see how to do it for the sake of curiosity.

Added

Since the built-in types are treated specially, here's an example with object where I just maintain a list object and implement my own get and set behavior to emulate list.

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 this case,

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:

But instead, if I ensure iteration stops and always returns 3, I can get what I was shooting to play around with in the first case:

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)

Then I see this, which is what I originally expected:

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]

Summary

  1. The syntax *args relies on iteration only. For built-in types this happens in a way that is not directly overrideable in classes that inherit from the built-in type.

  2. These two are functionally equivalent:

    foo(*[x for x in args])

    foo(*args)

  3. These are not equivalent even for finite data structures.

    foo(*args)

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

解决方案

You have been bitten by one of Python's most irritating warts: builtin types and subclasses of them are treated magically in some places.

Since your type subclasses from list, Python magically reaches into its internals to unpack it. It doesn't use the real iterator API at all. If you insert print statements inside your next and __getitem__, you'll see that neither one is being called. This behavior cannot be overridden; instead, you would have to write your own class that reimplements the builtin types. You could try using UserList; I haven't checked whether that would work.

The answer to your question is that argument unpacking uses iteration. However, iteration itself can use __getitem__ if there is no explicit __iter__ defined. You can't make a class that defines argument-unpacking behavior that is different from the normal iteration behavior.

The iterator protocol (basically "how __iter__ works") shouldn't be assumed to apply to types that subclass builtin types like list. If you subclass a builtin, your subclass may magically behave like the underlying builtin in certain situations, without making use of your customize magic methods (like __iter__). If you want to customize behavior fully and reliably, you can't subclass from builtin types (except, of course, object).

这篇关于参数是否拆包使用迭代或得到项目,?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆