我正在使用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!
因此,參數un打包以某種方式獲取來自zz
的項目數據,但不是通過迭代該對象及其實現的迭代器,也不是通過做一個可憐的人的迭代器,並且爲對象所具有的項目調用__getitem__
。
所以問題是:如果不是這些方法,語法add3(*zz)
如何獲取zz
的數據成員?我是否錯過了另一種從這種類型獲取數據成員的常見模式?
我的目標是看看我是否可以編寫一個實現迭代或項目獲取的類,以這種方式改變參數拆包語法對該類的意義。在嘗試了上述兩個示例之後,我現在想知道如何解開參數來獲取底層數據以及程序員是否可以影響該行爲。谷歌爲此僅回饋了大量的結果,解釋了*args
語法的基本用法。
我沒有一個用例需要做到這一點,我不認爲這是一個好主意。我只是想看看如何爲了好奇而做到這一點。
新增
由於內置類型都經過特殊處理,在這裏與object
一個例子,我只是保持一個列表對象,並實現自己的獲取和設置的行爲模仿名單。
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]
摘要
語法
*args
僅依賴於迭代。對於內置類型,這種方式不能直接在從內置類型繼承的類中被覆蓋。這兩個在功能上等效:
foo(*[x for x in args])
foo(*args)
這些都不是即使對於有限的數據結構等效。
foo(*args)
foo(*[args[i] for i in range(len(args))])
我想你會發現更有趣的信息,如果你從'object'而不是'list'派生'FooList'。 –
這不會因爲無限迭代而掛起;它會因爲__iter __(self)以遞歸方式間接調用自己而掛起,所以它甚至需要無限次遞歸_get_迭代器。你可以完全刪除'next'並獲得相同的行爲...... – abarnert
我還創建了一個單獨的'FooListIter'類,並且'__iter__'返回了一個實例,具有與上面相同的'next'行爲,並且它也執行了相同的操作。但是,謝謝你提到'iter'在這裏自稱。 – ely