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
このような型からデータ メンバーを取得するための別の一般的なパターンが 1 つ欠けているだけですか?
私の目標は、イテレーションまたはアイテムの取得を実装するクラスを記述して、引数のアンパック構文がそのクラスにとって何を意味するかを変更できるかどうかを確認することです。上記の 2 つの例を試した後、引数のアンパッキングが基になるデータを取得する方法と、プログラマーがその動作に影響を与えることができるかどうかが気になりました。*args
これについての Google は、構文の基本的な使用法を説明する大量の結果を返すだけでした。
これを行う必要があるユースケースはありませんし、それが良い考えだとは主張していません。好奇心のためにそれを行う方法を見たいだけです。
追加した
組み込み型は特別に扱われる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]
概要
構文
*args
は反復のみに依存します。組み込み型の場合、これは、組み込み型から継承するクラスで直接オーバーライドできない方法で発生します。これら 2 つは機能的に同等です。
foo(*[x for x in args])
foo(*args)
これらは、有限のデータ構造であっても同等ではありません。
foo(*args)
foo(*[args[i] for i in range(len(args))])