Bir jeneratörden sadece bir öğe nasıl seçilir?


213

Aşağıdaki gibi bir jeneratör işlevi var:

def myfunct():
  ...
  yield result

Bu işlevi çağırmanın olağan yolu:

for r in myfunct():
  dostuff(r)

Sorum, istediğim zaman jeneratörden sadece bir eleman almanın bir yolu var mı? Örneğin, şöyle bir şey yapmak istiyorum:

while True:
  ...
  if something:
      my_element = pick_just_one_element(myfunct())
      dostuff(my_element)
  ...

Yanıtlar:


304

Kullanarak jeneratör oluşturma

g = myfunct()

Her ürün istediğinizde şunu kullanın:

next(g)

(veya g.next()Python 2.5 veya altında).

Jeneratör çıkarsa yükselir StopIteration. Gerekirse bu özel durumu yakalayabilir veya defaultbağımsız değişkeni şu amaçlarla kullanabilirsiniz next():

next(g, default_value)

4
Not, StopIteration öğesini yalnızca g'deki son öğe sağlandıktan sonra g.next () yöntemini kullanmaya çalıştığınızda yükseltir.
Wilduck

26
next(gen, default)StopIterationistisnadan kaçınmak için de kullanılabilir . Örneğin next(g, None), bir dizgiler jeneratörü için yineleme bittikten sonra bir dize veya Yok verilir.
Attila

8
Python 3000'de, sonraki () __sonra __ ()
Jonathan Baldwin

27
@JonathanBaldwin: Yorumunuz biraz yanıltıcı. Python 3'te cevabımda verilen ikinci sözdizimini kullanırsınız next(g). Bu dahili olarak arayacaktır g.__next__(), ancak bunun için endişelenmenize gerek yoktur, tıpkı len(a)dahili olarak aramaları umursamadığınız gibi a.__len__().
Sven Marnach

14
Daha açık olmalıydım. g.next()olduğu g.__next__()Py3k içinde. Yerleşik next(iterator), Python 2.6'dan beri var ve tüm yeni Python kodlarında kullanılması gereken şeydir ve py <= 2.5'i desteklemeniz gerekiyorsa geri uygulama için önemsizdir.
Jonathan Baldwin

29

breakBir forifadede bir jeneratör kullanımının yalnızca bir öğesini seçmek için veyalist(itertools.islice(gen, 1))

Örneğinize göre (tam anlamıyla) şöyle bir şey yapabilirsiniz:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

"İstediğiniz zaman [bir kez oluşturulan] jeneratör sadece bir eleman olsun " istiyorsanız (Ben orijinal niyet ve en yaygın niyet% 50 thats varsayalım) o zaman:

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

Bu şekilde açıkça kullanımdan generator.next()kaçınılabilir ve giriş sonu işlemesi (şifreli) StopIterationistisna işleme veya ekstra varsayılan değer karşılaştırmaları gerektirmez.

else:Ait forsonuna-of-jeneratör durumunda şey special yapmak istiyorsanız deyimi bölümünde sadece ihtiyaç vardır.

next()/ İle ilgili not .next():

Python3'te .next()yöntem .__next__()iyi bir nedenle yeniden adlandırıldı : düşük seviye olarak kabul edildi (PEP 3114). Python 2.6'dan önce yerleşik işlev next()yoktu. Ve nadiren ihtiyacı ve yerleşik isimlerin enflasyonu şüpheli olduğu next()için operatormodüle (akıllıca olurdu) taşınması bile tartışıldı .

next()Varsayılan olmadan kullanmak hala çok düşük seviyeli bir uygulamadır - şifreli StopIterationgibi bir şifreyi normal uygulama kodunda maviden açık bir şekilde atmak . Ve doğrudan next()varsayılan için tek seçenek en iyi olan varsayılan sentinel ile kullanmak sınırlıdır ve genellikle garip pythonic olmayan mantık / okunabilirliğe neden olur.next()builtins

Alt satır: next () yönteminin kullanılması, operatormodülün işlevlerini kullanmak gibi çok nadir olmalıdır . Kullanılması for x in iterator, islice, list(iterator)oldukça her zaman mümkün ve - ve diğer fonksiyonlar sorunsuz bir yineleyici kabul uygulama düzeyinde iterators kullanmanın doğal bir yoldur. next()düşük seviyeli, ekstra bir kavram, açık değil - bu konunun sorusunun gösterdiği gibi. Örneğin breakin forkullanımı gelenekseldir.


8
Bu, sadece bir liste sonucunun ilk elemanını elde etmek için çok fazla çalışmadır. Genellikle yeterince tembel olmak zorunda değilim ama py3'te bir seçeneğim yok. Benzer bir şey yok mySeq.headmu?
javadba

2

Bir jeneratörden keyfi bir değer almanın uygun bir yolu olduğuna inanmıyorum. Jeneratör, kendi içinden geçmek için bir next () yöntemi sağlayacaktır, ancak hafızayı kurtarmak için tam dizi hemen üretilmez. Jeneratör ve liste arasındaki fonksiyonel fark budur.


1

Python3 için tam bir çalışma örneği için bu cevapları tarayanlar için ... işte buradasınız:

def numgen():
    x = 1000
    while True:
        x += 1
        yield x

nums = numgen() # because it must be the _same_ generator

for n in range(3):
    numnext = next(nums)
    print(numnext)

Bu çıktılar:

1001
1002
1003

1

Jeneratör, yineleyici üreten bir işlevdir. Bu nedenle, yineleyici örneğiniz varsa, yineleyiciden sonraki öğeyi almak için next () kullanın. Örnek olarak, ilk öğeyi getirmek için next () işlevini kullanın ve daha sonra for inkalan öğeleri işlemek için kullanın :

# create new instance of iterator by calling a generator function
items = generator_function()

# fetch and print first item
first = next(items)
print('first item:', first)

# process remaining items:
for item in items:
    print('next item:', item)

0
generator = myfunct()
while True:
   my_element = generator.next()

son eleman alındıktan sonra atılan istisnayı yakaladığınızdan emin olun


Python 3 için geçerli değil, kxr'nin mükemmel cevabına bakın .
clacke

2
Python 3 için "generator.next ()" yerine "next (generator)" yerine
geçmeniz yeterli

-3

Tek yolu yineleyici bir liste almak ve sonra bu listeden istediğiniz öğeyi almak olduğuna inanıyorum.

l = list(myfunct())
l[4]

Sven'in cevabı muhtemelen daha iyi, ama ihtiyaçlarınızla daha fazla satır içi olması durumunda bunu burada bırakacağım.
keegan3d

26
Bunu yapmadan önce sonlu bir jeneratörünüz olduğundan emin olun.
Seth

6
Üzgünüz, iteratörün uzunluğu karmaşıktır, sorun açıkçası O (1).
yo'

1
Jeneratör emmek için çok fazla bellek ve işlem israf! Ayrıca, @Seth'in daha önce de belirtildiği gibi, jeneratörlerin ne zaman durdurulacağı garanti edilmez.
pylover

Bu besbelli değil (veya eğer iyi yolu tek yol myfunct()kullanabileceğiniz beri değerlerin çok sayıda üretir) yerleşik işlevi nextsonraki oluşturulan değeri alır.
HelloGoodbye
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.