Ne yield
yaptığını anlamak için jeneratörlerin ne olduğunu anlamalısınız . Ve jeneratörleri anlayabilmeniz için, tekrarlanabilirleri anlamalısınız .
Iterables
Bir liste oluşturduğunuzda, öğelerini tek tek okuyabilirsiniz. Öğelerini tek tek okumaya yineleme denir:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
mylist
bir yinelenebilir . Bir liste kavrayışı kullandığınızda, bir liste oluşturur ve böylece yinelenebilir:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
" for... in...
" Üzerinde kullanabileceğiniz her şey yinelenebilir; lists
,, strings
dosyalar ...
Bu tekrarlamalar kullanışlıdır, çünkü onları istediğiniz kadar okuyabilirsiniz, ancak tüm değerleri hafızada saklarsınız ve çok fazla değeriniz olduğunda bu her zaman istediğiniz şey değildir.
Jeneratörler
Jeneratörler yineleyicilerdir, sadece bir kez yineleyebileceğiniz bir çeşit yinelenebilir . Jeneratörler tüm değerleri hafızada depolamaz, değerleri anında üretir :
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
Bunun ()
yerine kullandığınız dışında sadece aynı []
. ANCAK, sen olamaz gerçekleştirmekfor i in mygenerator
onlar 0 hesaplamak, sonra bunu unutur ve 1 hesaplamak ve teker 4, tek hesaplama sona: jeneratörler sadece kullanılabilir beri kez ikinci kez.
Yol ver
yield
return
işlevi bir jeneratör döndürecek hariç, gibi kullanılan bir anahtar kelimedir .
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
Burada işe yaramaz bir örnek, ancak işlevinizin yalnızca bir kez okumanız gereken büyük bir değer kümesi döndüreceğini bildiğinizde kullanışlıdır.
Master olarak yield
, işlevi çağırdığınızda, işlev gövdesinde yazdığınız kodun çalışmadığını anlamalısınız . Fonksiyon sadece jeneratör nesnesini döndürür, bu biraz zor :-)
Ardından, kodunuz her seferinde kaldığı yerden devam eder for
jeneratörü kullandığında .
Şimdi zor kısmı:
for
Fonksiyonunuzdan oluşturulan jeneratör nesnesini ilk kez çağırdığında, fonksiyonunuzdaki kodu baştan sona kadar çalıştıracak yield
, daha sonra döngünün ilk değerini döndürecektir. Ardından, sonraki her çağrı, işlevde yazdığınız döngünün başka bir yinelemesini çalıştırır ve bir sonraki değeri döndürür. Bu, jeneratör boş olarak kabul edilene kadar devam eder, bu da işlev çarpmadan çalışır yield
. Bunun nedeni, döngünün sona ermesi veya artık bir "if/else"
.
Kodunuz açıklandı
Jeneratör:
# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):
# Here is the code that will be called each time you use the generator object:
# If there is still a child of the node object on its left
# AND if the distance is ok, return the next child
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
# If there is still a child of the node object on its right
# AND if the distance is ok, return the next child
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
# If the function arrives here, the generator will be considered empty
# there is no more than two values: the left and the right children
Arayan:
# Create an empty list and a list with the current object reference
result, candidates = list(), [self]
# Loop on candidates (they contain only one element at the beginning)
while candidates:
# Get the last candidate and remove it from the list
node = candidates.pop()
# Get the distance between obj and the candidate
distance = node._get_dist(obj)
# If distance is ok, then you can fill the result
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
# Add the children of the candidate in the candidate's list
# so the loop will keep running until it will have looked
# at all the children of the children of the children, etc. of the candidate
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
Bu kod birkaç akıllı parça içerir:
Döngü bir liste üzerinde yinelenir, ancak döngü yinelenirken liste genişler :-) Sonsuz bir döngü ile sonuçlanabileceğiniz için biraz tehlikeli olsa bile tüm bu iç içe geçmiş verileri gözden geçirmek için özlü bir yoldur. Bu durumda, candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
jeneratörün tüm değerlerini boşaltın, ancak while
aynı düğüme uygulanmadığı için öncekilerden farklı değerler üretecek yeni jeneratör nesneleri oluşturmaya devam ediyor.
extend()
Yöntem, bir iterable bekler ve listeye değerlerini ekleyen bir liste nesnesinin bir yöntemdir.
Genellikle bir liste geçiririz:
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]
Ancak kodunuzda, bir jeneratör alır, bu iyidir:
- Değerleri iki kez okumak zorunda değilsiniz.
- Çok fazla çocuğunuz olabilir ve hepsinin hafızada saklanmasını istemezsiniz.
Ve çalışır çünkü Python bir yöntemin argümanının bir liste olup olmadığını umursamaz. Python yinelemeleri bekler, böylece dizeler, listeler, tuples ve jeneratörlerle çalışır! Buna ördek yazma denir ve Python'un bu kadar havalı olmasının nedenlerinden biridir. Ama bu başka bir hikaye, başka bir soru için ...
Burada durdurabilir veya bir jeneratörün gelişmiş kullanımını görmek için biraz okuyabilirsiniz:
Jeneratör tükenmesini kontrol etme
>>> class Bank(): # Let's create a bank, building ATMs
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
Not: Python 3 için print(corner_street_atm.__next__())
veyaprint(next(corner_street_atm))
Bir kaynağa erişimi kontrol etmek gibi çeşitli şeyler için yararlı olabilir.
Itertools, en iyi arkadaşın
İtertools modülü, yinelenebilirleri işlemek için özel işlevler içerir. Hiç bir jeneratörü çoğaltmak istediniz mi? İki jeneratör zinciri mi? Yuvalanmış bir listede değerleri bir astarla gruplandır? Map / Zip
başka bir liste oluşturmadan?
O zaman sadece import itertools
.
Bir örnek? Dört atlı bir yarış için olası varış emirlerini görelim:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
İterasyonun iç mekanizmalarını anlama
Yineleme, yinelemeleri ( __iter__()
yöntemi uygulama ) ve yinelemeleri ( yöntemi uygulama ) ima eden bir süreçtir __next__()
. Yinelemeler, yineleyici alabileceğiniz herhangi bir nesnedir. Yineleyiciler yinelemeleri tekrarlamanıza izin veren nesnelerdir.
Bu makalede döngülerin nasıl for
çalıştığı hakkında daha fazla bilgi var .
yield
bu cevabın önerdiği kadar büyülü değil. Herhangi biryield
yerde ifade içeren bir işlevi çağırdığınızda , bir jeneratör nesnesi alırsınız, ancak kod çalışmaz . Daha sonra bir nesneyi jeneratörden her çıkardığınızda, Python biryield
deyime gelinceye kadar işlevdeki kodu yürütür , sonra nesneyi duraklatır ve teslim eder. Başka bir nesneyi çıkardığınızda, Python nesneden hemen sonra devam ederyield
ve başka bir nesneye ulaşana kadar devam ederyield
(genellikle aynı, ancak bir yineleme daha sonra). Bu, fonksiyon bitene kadar devam eder, bu noktada jeneratör tükenmiş kabul edilir.