Yineleyiciler ve jeneratörler arasındaki fark nedir? Her vakayı ne zaman kullanacağınıza dair bazı örnekler yardımcı olacaktır.
Yineleyiciler ve jeneratörler arasındaki fark nedir? Her vakayı ne zaman kullanacağınıza dair bazı örnekler yardımcı olacaktır.
Yanıtlar:
iterator
olan sınıf olan herhangi bir nesne: daha genel bir kavramdır next
(yöntem __next__
Python 3) ve __iter__
yapar yöntem return self
.
Her jeneratör bir yineleyicidir, ancak tersi değildir. Bir veya daha fazla yield
ifadeye ( yield
Python 2.5 ve önceki sürümlerinde ifadeler) sahip bir işlev çağırarak oluşturucu oluşturulur ve önceki paragrafın bir iterator
.
Biraz karmaşık durum koruma davranışına sahip bir sınıfa ihtiyacınız olduğunda veya next
( __iter__
ve __init__
) dışında başka yöntemler göstermek istediğinizde, bir jeneratör yerine özel bir yineleyici kullanmak isteyebilirsiniz . Çoğu zaman, bir jeneratör (bazen, yeterince basit ihtiyaçlar için, bir jeneratör ifadesi ) yeterlidir ve kodlanması daha kolaydır, çünkü durum bakımı (makul sınırlar dahilinde) temel olarak çerçevenin askıya alınması ve devam ettirilmesi tarafından "sizin için yapılır".
Örneğin, aşağıdaki gibi bir jeneratör:
def squares(start, stop):
for i in range(start, stop):
yield i * i
generator = squares(a, b)
veya eşdeğer jeneratör ifadesi (genexp)
generator = (i*i for i in range(a, b))
özel bir yineleyici olarak oluşturmak için daha fazla kod gerekir:
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def next(self): # __next__ in Python 3
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
iterator = Squares(a, b)
Ancak, elbette, sınıfla Squares
kolayca ekstra yöntemler sunabilirsiniz, yani
def current(self):
return self.start
uygulamanızda bu tür ekstra işlevlere gerçekten ihtiyacınız varsa.
for ... in ...:
iter.next()
for..in
sözdizimini kullanmaya çalışırken hata alıyordum . Belki bir şey eksikti, ama bir süre önce, çözdüğümü hatırlamıyorum. Teşekkür ederim!
Yineleyiciler ve jeneratörler arasındaki fark nedir? Her vakayı ne zaman kullanacağınıza dair bazı örnekler yardımcı olacaktır.
Özetle: Yineleyiciler, __iter__
ve __next__
( next
Python 2'de) yöntemine sahip nesnelerdir . Jeneratörler, Yineleyiciler örnekleri oluşturmak için kolay ve yerleşik bir yol sağlar.
İçinde verim olan bir işlev, çağrıldığında bir jeneratör nesnesinin bir örneğini döndüren bir işlevdir:
def a_function():
"when called, returns generator object"
yield
Bir jeneratör ifadesi ayrıca bir jeneratör döndürür:
a_generator = (i for i in range(0))
Daha ayrıntılı bir açıklama ve örnekler için okumaya devam edin.
Özellikle, jeneratör yineleyicinin bir alt türüdür.
>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
Jeneratörü birkaç yolla oluşturabiliriz. Bunu yapmanın çok yaygın ve basit bir yolu bir işlevdir.
Özellikle, içinde verim olan bir işlev, çağrıldığında bir jeneratör döndüren bir işlevdir:
>>> def a_function():
"just a function definition with yield in it"
yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function() # when called
>>> type(a_generator) # returns a generator
<class 'generator'>
Ve yine bir jeneratör bir Yineleyici:
>>> isinstance(a_generator, collections.Iterator)
True
Bir Yineleyici Yinelenebilir,
>>> issubclass(collections.Iterator, collections.Iterable)
True
hangi gerektirir __iter__
bir Yineleyici döndüren bir yöntem :
>>> collections.Iterable()
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__
Tekrarlanabilir bazı örnekler yerleşik tuples, listeler, sözlükler, kümeler, dondurulmuş kümeler, dizeler, bayt dizeleri, bayt dizileri, aralıklar ve bellek görünümleridir:
>>> all(isinstance(element, collections.Iterable) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
next
veya __next__
yöntemPython 2'de:
>>> collections.Iterator()
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next
Ve Python 3'te:
>>> collections.Iterator()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__
Yineleyicileri iter
işlevli yerleşik nesnelerden (veya özel nesnelerden) alabiliriz :
>>> all(isinstance(iter(element), collections.Iterator) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
__iter__
Eğer bir dongu ile bir nesneyi kullanma girişiminde yöntemi olarak adlandırılır. Sonra__next__
döngü için her öğeyi almak için yineleyici nesnesinde yöntem çağrılır. Yineleyici, StopIteration
tükendiğinde yükselir ve bu noktada tekrar kullanılamaz.
Yerleşik Türler belgelerinin Yineleyici Türleri bölümünün Jeneratör Türleri bölümünden :
Python'un jeneratörleri, yineleyici protokolünü uygulamak için uygun bir yol sağlar. Bir kapsayıcı nesnesinin
__iter__()
yöntemi bir oluşturucu olarak uygulanırsa,__iter__()
venext()
[__next__()
Python 3'te] yöntemlerini sağlayan bir yineleyici nesnesini (teknik olarak bir oluşturucu nesnesi) otomatik olarak döndürür . Jeneratörler hakkında daha fazla bilgi verim ifadesi belgelerinde bulunabilir.
(Vurgu eklendi.)
Bundan dolayı Jeneratörlerin (uygun) bir Yineleyici olduğunu öğreniyoruz.
Kendi nesnenizi oluşturarak veya genişleterek Iterator protokolünü uygulayan nesne oluşturabilirsiniz.
class Yes(collections.Iterator):
def __init__(self, stop):
self.x = 0
self.stop = stop
def __iter__(self):
return self
def next(self):
if self.x < self.stop:
self.x += 1
return 'yes'
else:
# Iterators must raise when done, else considered broken
raise StopIteration
__next__ = next # Python 3 compatibility
Ancak bunu yapmak için bir Jeneratör kullanmak daha kolaydır:
def yes(stop):
for _ in range(stop):
yield 'yes'
Ya da belki daha basit bir Jeneratör İfadesi (liste kavrayışlarına benzer şekilde çalışır):
yes_expr = ('yes' for _ in range(stop))
Hepsi aynı şekilde kullanılabilir:
>>> stop = 4
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop),
('yes' for _ in range(stop))):
... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes
Tekrarlanabilir bir nesne olarak bir Python nesnesini genişletmeniz gerektiğinde Iterator protokolünü doğrudan kullanabilirsiniz.
Bununla birlikte, vakaların büyük çoğunluğunda, kullanmak için en uygunudur yield
bir Jeneratör Yineleyicisi döndüren veya Jeneratör İfadelerini dikkate alan bir işlevi tanımlamak yöntemdir.
Son olarak, jeneratörlerin koroutin olarak daha fazla işlevsellik sağladığını unutmayın. Ben birlikte Jeneratörler açıklamak yield
verim "ne yok‘’do Keyword?" Cevabım üzerine derinlemesine, açıklamada.
yineleyiciler:
Yineleyici, next()
sıralamanın bir sonraki değerini almak yöntem .
Jeneratörler:
Jeneratör, bir dizi değer üreten veya üreten bir işlevdir. yield
yöntem .
Jeneratör işlevi tarafından döndürülen her next()
yöntem çağrısı ( f
aşağıdaki örnekte olduğu gibi ex için ) ( foo()
aşağıdaki örnekte ex: işlevi için ), sırayla bir sonraki değeri üretir.
Bir jeneratör işlevi çağrıldığında, işlevin yürütülmesine bile başlamadan bir jeneratör nesnesi döndürür. Tüm next()
yöntem, ilk kez olarak adlandırılır bu vermiştir değerini verir verim deyimi ulaşana kadar, fonksiyon yürütmeye başlar. Verim, yani son yürütmeyi hatırlar. Ve ikinci next()
çağrı önceki değerden devam eder.
Aşağıdaki örnek, üretken nesnesindeki verim ve sonraki yönteme çağrı arasındaki etkileşimi gösterir.
>>> def foo():
... print "begin"
... for i in range(3):
... print "before yield", i
... yield i
... print "after yield", i
... print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0 # Control is in for loop
0
>>> f.next()
after yield 0
before yield 1 # Continue for loop
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Mevcut cevapların hiçbiri resmi literatürdeki karışıklığa özellikle değinmediği için bir cevap eklemek.
Jeneratör işlevleriyield
yerinekullanılarak tanımlanan sıradan işlevlerdirreturn
. Bir jeneratör işlevi çağrıldığında, birtür yineleyici olanbir jeneratör nesnesini döndürür- birnext()
yöntemi vardır. Aradığınızdanext()
, jeneratör işlevi tarafından verilen bir sonraki değer döndürülür.
Okuduğunuz Python kaynak belgesine bağlı olarak ya işlev ya da nesne "jeneratör" olarak adlandırılabilir. Python sözlüğü olurken, jeneratör fonksiyonlarını diyor Python wiki jeneratör nesneleri ima eder. Python öğretici oldukça ima etmek yöneten hem üç cümle uzayda kullanımlarını:
Jeneratörler, yineleyiciler oluşturmak için basit ve güçlü bir araçtır. Normal işlevler gibi yazılırlar ancak veri döndürmek istedikleri zaman getiri ifadesini kullanırlar. Next () işlevinin her çağrılışında jeneratör kaldığı yerden devam eder (tüm veri değerlerini ve en son hangi cümleyi çalıştırdığını hatırlar).
İlk iki cümle jeneratör fonksiyonuna sahip jeneratörleri, üçüncü cümle ise jeneratör nesnelerini tanımlar.
Tüm bu karışıklığa rağmen , açık ve son kelime için Python dil referansını araştırabilirsiniz:
Verim ifadesi yalnızca bir jeneratör işlevi tanımlanırken kullanılır ve yalnızca bir işlev tanımının gövdesinde kullanılabilir. Bir işlev tanımında bir verim ifadesi kullanmak, bu tanımın normal bir işlev yerine bir üreteç işlevi oluşturmasına neden olmak için yeterlidir.
Bir jeneratör işlevi çağrıldığında, jeneratör olarak bilinen bir yineleyici döndürür. Bu jeneratör daha sonra bir jeneratör fonksiyonunun yürütülmesini kontrol eder.
Yani, resmi ve kesin kullanımda, "jeneratör" niteliksiz jeneratör fonksiyonu değil, jeneratör nesnesi anlamına gelir.
Yukarıdaki referanslar Python 2 içindir, ancak Python 3 dil referansı aynı şeyi söyler. Ancak, Python 3 sözlüğü devletler bu
jeneratör ... Genellikle bir jeneratör işlevini belirtir, ancak bazı bağlamlarda bir jeneratör yineleyicisini ifade edebilir. Amaçlanan anlamın net olmadığı durumlarda, tam terimlerin kullanılması belirsizliği önler.
Herkesin örneklerle gerçekten güzel ve ayrıntılı bir cevabı var ve bunu gerçekten takdir ediyorum. Kavramsal olarak hala çok net olmayan insanlar için birkaç satır kısa cevap vermek istedim:
Kendi yineleyicinizi oluşturursanız, biraz dahil olur - bir sınıf oluşturmanız ve en azından yineleyiciyi ve sonraki yöntemleri uygulamanız gerekir. Ama ya bu güçlükten geçmek istemiyorsanız ve hızlı bir şekilde bir yineleyici oluşturmak istiyorsanız. Neyse ki, Python bir yineleyici tanımlamak için kısa bir yol sağlar. Yapmanız gereken tek şey, en az 1 verim çağrısı olan bir işlev tanımlamaktır ve şimdi bu işlevi çağırdığınızda, bir yineleyici gibi davranacak bir şey döndürür (sonraki yöntemi çağırabilir ve bir for döngüsünde kullanabilirsiniz). Bu şeyin Python'da Generator adlı bir adı var
Umarım bu biraz açıklığa kavuşur.
Önceki cevaplar bu eklemeyi kaçırdı: bir jeneratörün bir close
yöntemi var, ancak tipik yineleyiciler yok. close
Yöntem tetikler StopIteration
a yakalanmış olabilir jeneratörü istisna,finally
o yineleyici fıkra, bazı temizlik çalıştırmak için bir şans için. Bu soyutlama onu basit yineleyicilerden çok daha büyük oranda kullanılabilir hale getirir. Bir jeneratör, altında ne olduğunu rahatsız etmek zorunda kalmadan bir dosyayı kapatabileceği gibi kapatabilir.
Bununla birlikte, ilk soruya kişisel cevabım şöyle olurdu: yinelenebilir __iter__
sadece bir yöntem var, tipik yineleyiciler sadece bir __next__
yöntem var , jeneratörler hem bir hem de bir __iter__
ve __next__
ekclose
.
İkinci soru için, kişisel cevabım şöyle olurdu: halka açık bir arayüzde, daha esnek olduğu için jeneratörleri tercih etme eğilimindeyim: close
yöntem daha büyük bir bileşimyield from
. Yerel olarak yineleyicileri kullanabilirim, ancak yalnızca düz ve basit bir yapı (yineleyiciler kolayca oluşturmazsa) ve dizinin özellikle sona ulaşmadan durdurulabileceğine inanmak için nedenler varsa. Yineleyicilere değişmez değerler dışında düşük düzeyli bir ilkel olarak bakma eğilimindeyim.
Kontrol akışıyla ilgili konularda, jeneratörler vaatler kadar önemli bir kavramdır: her ikisi de soyut ve oluşturulabilir.
__iter__
yöntemi olduğundan, yineleyici nasıl olabilir __next__
? Yinelenebilir olmaları gerekiyorsa, onların da mutlaka olmasını beklerdim __iter__
.
__iter__
, yalnızca bir next
yöntem ( __next__
Python3'te) gerektiren bir yineleyiciyi döndürmek için yalnızca bir yinelenebilir açık gerektirir . Lütfen standartları (ördek yazmak için) uygulamalarıyla (belirli bir Python yorumlayıcısının nasıl uyguladığı) karıştırmayın. Bu biraz jeneratör fonksiyonları (tanım) ve jeneratör nesneleri (uygulama) arasındaki karışıklığa benzer. ;)
Jeneratör İşlevi, Jeneratör Nesnesi, Jeneratör:
Bir Generator işlevi Python'daki normal bir işlev gibidir, ancak bir veya daha fazla yield
ifade içerir . Jeneratör fonksiyonları mümkün olduğunca kolay Iterator nesneleri oluşturmak için harika bir araçtır . Yineleyici jeneratör işlevi nesne returend da adlandırılır Jeneratör nesne ya da jeneratör .
Bu örnekte bir Generator nesnesi döndüren bir Generator işlevi oluşturdum <generator object fib at 0x01342480>
. Diğer yineleyiciler gibi, Generator nesneleri de bir for
döngüde veya next()
jeneratörden sonraki değeri döndüren yerleşik işlevle kullanılabilir.
def fib(max):
a, b = 0, 1
for i in range(max):
yield a
a, b = b, a + b
print(fib(10)) #<generator object fib at 0x01342480>
for i in fib(10):
print(i) # 0 1 1 2 3 5 8 13 21 34
print(next(myfib)) #0
print(next(myfib)) #1
print(next(myfib)) #1
print(next(myfib)) #2
Yani bir üreteç işlevi, bir Yineleme nesnesi oluşturmanın en kolay yoludur.
Yineleyici :
Her jeneratör nesnesi bir yineleyicidir, ancak tam tersi değildir. Sınıfı uygular __iter__
ve __next__
yöntemi (yineleyici protokolü olarak da bilinir) özel bir yineleyici nesnesi oluşturulabilir .
Ancak, jeneratörler fonksiyonu oluşturacağına kullanımı çok daha kolaydır yineleyicinızı onların oluşturulmasını basitleştirmek için değil, özel bir Yineleyici size daha fazla özgürlük verir ve aşağıdaki örnekte gösterildiği gibi siz de ihtiyaçlarına göre başka yöntemler uygulayabilir.
class Fib:
def __init__(self,max):
self.current=0
self.next=1
self.max=max
self.count=0
def __iter__(self):
return self
def __next__(self):
if self.count>self.max:
raise StopIteration
else:
self.current,self.next=self.next,(self.current+self.next)
self.count+=1
return self.next-self.current
def __str__(self):
return "Generator object"
itobj=Fib(4)
print(itobj) #Generator object
for i in Fib(4):
print(i) #0 1 1 2
print(next(itobj)) #0
print(next(itobj)) #1
print(next(itobj)) #1
Yin Batchelder'dan yineleyiciler ve jeneratörler için şiddetle tavsiye edilen örnekler
Sayılara bir şey yapan jeneratörsüz bir yöntem
def evens(stream):
them = []
for n in stream:
if n % 2 == 0:
them.append(n)
return them
bir jeneratör kullanarak
def evens(stream):
for n in stream:
if n % 2 == 0:
yield n
return
ifadeye ihtiyacımız yokevens
Yöntemi (jeneratör) çağırmak her zamanki gibi
num = [...]
for n in evens(num):
do_smth(n)
yineleyici
Sayfalarla dolu bir kitap yinelenebilir , Yer işareti yineleyicidir
ve bu yer iminin taşınmak dışında bir ilgisi yok next
litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration (Exception) as we got end of the iterator
Jeneratörü kullanmak için ... bir fonksiyona ihtiyacımız var
Yineleyici kullanmak için ... İhtiyacımız next
veiter
Söylendiği gibi:
Bir Generator işlevi bir iterator nesnesini döndürür
Yineleyicinin tüm yararı:
Bir öğeyi bir kerede bellekte saklama
Her iki yaklaşımı da aynı veriler için karşılaştırabilirsiniz:
def myGeneratorList(n):
for i in range(n):
yield i
def myIterableList(n):
ll = n*[None]
for i in range(n):
ll[i] = i
return ll
# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
print("{} {}".format(i1, i2))
# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
Ayrıca, bellek ayak izini kontrol ederseniz, jeneratör tüm değerleri aynı anda belleğe kaydetmeye gerek duymadığından çok daha az bellek alır.
Python'un yeni başlayanlar için çok basit bir şekilde yazıyorum, ancak Python'un derinliklerinde pek çok şey yapıyor.
Çok temel ile başlayalım:
Bir liste düşünün,
l = [1,2,3]
Eşdeğer bir işlev yazalım:
def f():
return [1,2,3]
o / p / print(l): [1,2,3]
& o / p /print(f()) : [1,2,3]
Listeyi tekrarlanabilir yapalım: Python listesinde her zaman yinelenebilir, bu da istediğiniz zaman yineleyici uygulayabileceğiniz anlamına gelir.
Şimdi yineleyiciyi listeye uygulayalım:
iter_l = iter(l) # iterator applied explicitly
Bir işlevi tekrarlanabilir hale getirelim, yani eşdeğer bir jeneratör işlevi yazalım.
Anahtar kelimeyi tanıttığınız anda python'da yield
; bir jeneratör işlevi haline gelir ve yineleyici dolaylı olarak uygulanır.
Not: Her jeneratörün her zaman üstü kapalı yineleyici uygulandığında tekrarlanabilir olması ve burada örtük yineleyicinin temel noktası Jeneratör işlevi şöyle olacaktır:
def f():
yield 1
yield 2
yield 3
iter_f = f() # which is iter(f) as iterator is already applied implicitly
Eğer gözlemlediyseniz, fa jeneratör işlevini yaptığınızda, zaten iter (f)
Şimdi,
l listedir, yineleyici yöntemi uyguladıktan sonra "iter" olur, iter (l)
f zaten iter (f) 'dir, iter yöntemi "iter" uygulandıktan sonra iter (iter (f)), tekrar iter (f)
Bu zaten int olan int (x) 'e döküm yapıyorsunuz ve int (x) olarak kalacak.
Örneğin:
print(type(iter(iter(l))))
dır-dir
<class 'list_iterator'>
Unutmayın, bu Python ve C veya C ++ değil
Dolayısıyla yukarıdaki açıklamadan çıkan sonuç şöyledir:
liste l ~ = iter (l)
üreteç işlevi f == iter (f)