Biraz karşılıklı etkileşimine daha fazla ışık biraz döken istiyorum iter, __iter__ve __getitem__ne perde arkasında olur. Bu bilgiyle donanmış olarak, en iyi neden yapabileceğinizi anlayabileceksiniz.
try:
iter(maybe_iterable)
print('iteration will probably work')
except TypeError:
print('not iterable')
Önce gerçekleri listeleyeceğim ve daha sonra python'da bir fordöngü kullandığınızda neler olduğunu hızlı bir hatırlatma ve ardından gerçekleri göstermek için bir tartışma izleyeceğim.
Gerçekler
Herhangi nesneden bir yineleyici alabilirsiniz oçağırarak iter(o): aşağıdaki koşullardan en az birinin de geçerlidir eğer
) bir obir sahip __iter__bir yineleyici nesnesi döndüren yöntemi. Yineleyici, __iter__ve __next__(Python 2:) nextyöntemine sahip herhangi bir nesnedir .
b) obir __getitem__yöntemi vardır.
IterableVeya öğesinin örneğini Sequencedenetlemek veya özniteliği denetlemek __iter__yeterli değildir.
Bir nesne ise ouygular sadece __getitem__değil __iter__, iter(o)çalışır öğeleri getirmesi olduğunu bir yineleyici inşa edecek oindex 0 dan başlayarak tamsayı endeksine göre yineleyici herhangi yakalayacak IndexErroryükseltilmiş olması (ama başka hatalar) ve daha sonra yükseltir StopIterationkendisini.
En genel anlamda, geri dönen yineleyicinin iterdenemek dışında aklı başında olup olmadığını kontrol etmenin bir yolu yoktur .
Bir nesne ouygulanırsa __iter__, iterişlev, döndürülen nesnenin __iter__bir yineleyici olduğundan emin olur . Sadece bir cismin uygulanıp uygulanmadığı konusunda bir akıl sağlığı kontrolü yoktur __getitem__.
__iter__kazanır. Bir nesne ise ouygular hem __iter__ve __getitem__, iter(o)arayacak __iter__.
Kendi nesnelerinizi yinelenebilir hale getirmek istiyorsanız, her zaman __iter__yöntemi uygulayın.
for döngüler
Takip etmek için for, Python'da bir döngü kullandığınızda ne olduğunu anlamanız gerekir . Zaten biliyorsanız, bir sonraki bölüme geçmekten çekinmeyin.
for item in oBazı yinelenebilir nesne için kullandığınızda o, Python iter(o)bir yineleyici nesnesini döndürür ve dönüş değeri olarak bekler. Yineleyici, __next__(veya nextPython 2'de) bir yöntem ve yöntem uygulayan herhangi bir nesnedir __iter__.
Geleneksel olarak, __iter__bir yineleyici yöntem nesnenin kendisi (yani döndürmelidir return self). Python , yükseltilene nextkadar yineleyiciyi çağırır StopIteration. Bütün bunlar dolaylı olarak gerçekleşir, ancak aşağıdaki gösteri onu görünür kılar:
import random
class DemoIterable(object):
def __iter__(self):
print('__iter__ called')
return DemoIterator()
class DemoIterator(object):
def __iter__(self):
return self
def __next__(self):
print('__next__ called')
r = random.randint(1, 10)
if r == 5:
print('raising StopIteration')
raise StopIteration
return r
Üzerinde yineleme DemoIterable:
>>> di = DemoIterable()
>>> for x in di:
... print(x)
...
__iter__ called
__next__ called
9
__next__ called
8
__next__ called
10
__next__ called
3
__next__ called
10
__next__ called
raising StopIteration
Tartışma ve çizimler
1. ve 2. noktalarda: yineleyici ve güvenilir olmayan kontroller almak
Aşağıdaki sınıfı düşünün:
class BasicIterable(object):
def __getitem__(self, item):
if item == 3:
raise IndexError
return item
Örneğinin çağrılması iter, uygulayıcılar BasicIterablenedeniyle sorunsuz bir yineleyici döndürür .BasicIterable__getitem__
>>> b = BasicIterable()
>>> iter(b)
<iterator object at 0x7f1ab216e320>
Ancak, o notta önemlidir byoktur __iter__özelliğini ve bir örneği olarak kabul edilmez Iterableya Sequence:
>>> from collections import Iterable, Sequence
>>> hasattr(b, '__iter__')
False
>>> isinstance(b, Iterable)
False
>>> isinstance(b, Sequence)
False
Bu nedenle Luciano Ramalho'dan Fluent Python , bir nesnenin yinelenebilir olup olmadığını kontrol etmenin en doğru yol olarak iterpotansiyelin çağrılmasını ve ele alınmasını TypeErrorönerir. Doğrudan kitaptan alıntı:
Python x3.4'ten itibaren, bir nesnenin yinelenebilir olup olmadığını kontrol etmenin en doğru yolu , değilse iter(x)bir TypeErroristisnayı çağırmak ve işlemektir . Bu, kullanmaktan daha doğrudur isinstance(x, abc.Iterable), çünkü ABC yöntemi iter(x)de eski __getitem__yöntemi dikkate alır Iterable.
3. nokta: Yalnızca sağlayan __getitem__ancak sağlamayan nesneler üzerinde yineleme__iter__
BasicIterableBeklendiği gibi bir çalışma örneği üzerinde yineleme : Python, bir IndexErroryükseltilene kadar sıfırdan başlayarak öğeleri dizine göre almaya çalışan bir yineleyici oluşturur. Demo nesnesinin __getitem__yöntemi , döndürülen yineleyici tarafından itemargüman olarak sağlanan yöntemi döndürür .__getitem__(self, item)iter
>>> b = BasicIterable()
>>> it = iter(b)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Yineleyicinin bir StopIterationsonraki öğeyi döndüremediğinde IndexErroryükseldiğini ve bunun için yükseltildiğini item == 3dahili olarak ele aldığını unutmayın . Bu nedenle BasicIterable, bir fordöngü ile döngü oluşturmak beklendiği gibi çalışır:
>>> for x in b:
... print(x)
...
0
1
2
Burada, yineleyicinin döndürdüğü kavramı iteröğelere dizine göre nasıl erişmeye çalıştığı kavramını yönlendirmek için başka bir örnek . WrappedDictmiras almaz dict, yani örneklerin bir __iter__yöntemi olmaz.
class WrappedDict(object): # note: no inheritance from dict!
def __init__(self, dic):
self._dict = dic
def __getitem__(self, item):
try:
return self._dict[item] # delegate to dict.__getitem__
except KeyError:
raise IndexError
Köşeli parantez notasyonunun sadece bir steno olduğu çağrılara __getitem__delege edildiğini unutmayın dict.__getitem__.
>>> w = WrappedDict({-1: 'not printed',
... 0: 'hi', 1: 'StackOverflow', 2: '!',
... 4: 'not printed',
... 'x': 'not printed'})
>>> for x in w:
... print(x)
...
hi
StackOverflow
!
4. ve 5. noktalarda: iterçağırdığında yineleyici olup olmadığını kontrol eder__iter__ :
Ne zaman iter(o)bir nesne için çağrılır o, iterdönüş değeri emin olacaktır __iter__yöntem, mevcut ise, Yineleyicinin. Bu, döndürülen nesnenin __next__(veya nextPython 2'de) uygulanması gerektiği anlamına gelir __iter__. iteryalnızca sağlayan nesneler için herhangi bir sağlık kontrolü gerçekleştiremez __getitem__, çünkü nesnenin öğelerine tamsayı dizini tarafından erişilip erişilemediğini kontrol etmenin bir yolu yoktur.
class FailIterIterable(object):
def __iter__(self):
return object() # not an iterator
class FailGetitemIterable(object):
def __getitem__(self, item):
raise Exception
FailIterIterableÖrneklerden bir yineleyici oluşturmanın derhal başarısız olduğunu ve yinelemeden yineleyici oluşturmanın FailGetItemIterablebaşarılı olduğunu, ancak ilk çağrıya bir İstisna atacağını unutmayın __next__.
>>> fii = FailIterIterable()
>>> iter(fii)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'object'
>>>
>>> fgi = FailGetitemIterable()
>>> it = iter(fgi)
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/path/iterdemo.py", line 42, in __getitem__
raise Exception
Exception
6. noktada: __iter__kazanır
Bu çok açık. Bir nesne uygular ise __iter__ve __getitem__, iterarayacak __iter__. Aşağıdaki sınıfı düşünün
class IterWinsDemo(object):
def __iter__(self):
return iter(['__iter__', 'wins'])
def __getitem__(self, item):
return ['__getitem__', 'wins'][item]
ve bir örnek üzerinde döngü oluştururken çıktı:
>>> iwd = IterWinsDemo()
>>> for x in iwd:
... print(x)
...
__iter__
wins
7. nokta: yinelenebilir sınıflarınız __iter__
Kendinize neden listbir __iter__yöntem uygulamak gibi çoğu yerleşik dizinin __getitem__yeterli olacağını kendinize sorabilirsiniz .
class WrappedList(object): # note: no inheritance from list!
def __init__(self, lst):
self._list = lst
def __getitem__(self, item):
return self._list[item]
Sonuçta, yineleme delegeler aramalar için, yukarıda sınıfın örneklerini üzerinde __getitem__etmek list.__getitem__(köşeli ayraç gösterimi kullanılarak), cezası çalışacaktır:
>>> wl = WrappedList(['A', 'B', 'C'])
>>> for x in wl:
... print(x)
...
A
B
C
Özel tekrarlarınızın uygulanması gereken nedenler __iter__şunlardır:
- Eğer uygularsanız
__iter__, örnekler dikkate alınan Iterables olacak ve isinstance(o, collections.abc.Iterable)dönecektir True.
- Tarafından döndürülen nesne
__iter__yineleyici değilse, iterderhal başarısız olur ve a değerini yükseltir TypeError.
- Özel kullanım,
__getitem__geriye dönük uyumluluk nedeniyle vardır. Akıcı Python'dan tekrar alıntı:
Bu nedenle herhangi bir Python dizisi yinelenebilir: hepsi uygular __getitem__. Aslında, standart diziler de uygulanır __iter__ve sizinki de olmalıdır, çünkü __getitem__geriye dönük uyumluluk nedenleriyle özel işlemesi vardır ve gelecekte gidebilir (bunu yazarken reddedilmese de).
__getitem__bir nesneyi tekrarlanabilir hale getirmek için de yeterli