Bir yineleyicinin en az bir öğe verip vermediğini kontrol etmek için tek satırlık?


105

Şu anda bunu yapıyorum:

try:
    something = iterator.next()
    # ...
except StopIteration:
    # ...

Ama basit bir ifcümlenin içine yerleştirebileceğim bir ifade istiyorum. Bu kodu daha az beceriksiz gösterecek yerleşik bir şey var mı?

any()Falseyinelenebilir bir öğe boşsa döndürür , ancak değilse potansiyel olarak tüm öğeler üzerinde yineleme yapar. Sadece ilk maddeyi kontrol etmek için ihtiyacım var.


Biri ne yapmaya çalıştığımı soruyor. Bir SQL sorgusu çalıştıran ve sonuçlarını veren bir fonksiyon yazdım. Bazen bu işlevi çağırdığımda, sorgunun bir şey döndürüp döndürmediğini bilmek ve buna göre bir karar vermek istiyorum.


2
Ayrıca bu kodla ilgili bir sorun, onu bir fonksiyona paketleyememenizdir, çünkü ilk elementi yiyecek. İyi soru.
andrewrk

2
Benim durumumda elemana hiç ihtiyacım yok, sadece en az bir eleman olduğunu bilmek istiyorum.
Bastien Léonard

2
hah! Aynı çözümü bulmaya çalışırken aynı kullanım durumum!
Daniel


Yanıtlar:


137

anyTrue ise ilk öğenin ötesine geçmez. Yineleyicinin yanlış bir şey vermesi durumunda yazabilirsiniz any(True for _ in iterator).


Bu benim için bir re.finditer ile çalışıyor gibi görünüyor. İlk başta herhangi bir duruşu kolayca test edebilirsiniz: çalıştırın any((x > 100 for x in xrange(10000000)))ve sonra çalıştırın any((x > 10000000 for x in xrange(100000000)))- ikincisi çok daha uzun sürer.
chbrown

1
Bu "en az x" durumu için işe sum(1 for _ in itertools.islice(iterator, max_len)) >= max_len
Dave Butler

11
Benzer şekilde, yineleyicinin boş olup olmadığını kontrol etmeniz gerekirse, yineleyicinin boş all(False for _ in iterator)olup olmadığını kontrol edebilirsiniz. (Yineleyici boşsa tümü True döndürür, aksi takdirde ilk Yanlış öğeyi gördüğünde durur)
KGardevoir

24
Bu çözümle ilgili en büyük sorun, boş değilse yineleyiciden döndürülen değeri gerçekten kullanamayacağınızdır, değil mi?
Ken Williams

42

Python 2.6+ sürümünde, ad sentinelyineleyicinin veremeyeceği bir değere bağlıysa,

if next(iterator, sentinel) is sentinel:
    print('iterator was empty')

Yineleyicinin ne verebileceği konusunda hiçbir fikriniz yoksa, kendi nöbetçinizi yapın (örneğin modülünüzün tepesinde) ile

sentinel = object()

Aksi takdirde, nöbetçi rolünde, yineleyicinin veremeyeceği "bildiğiniz" (uygulama değerlendirmelerine dayalı olarak) herhangi bir değeri kullanabilirsiniz.


1
Güzel! Kullanım durumum için, if not next(iterator, None):Hiçbirinin öğelerin bir parçası olmayacağından emin olduğum için yeterliydi. Beni doğru yönü gösterdiğin için teşekkürler!
wasabigeek

1
@wasabi notBoş listeler, Yanlış ve sıfır gibi yanlış nesneler için True döndüreceğini unutmayın . is not Nonedaha güvenli ve bence daha net.
Caagr98

21

Bu gerçekten daha temiz değil, ancak onu bir işlevde kayıpsız bir şekilde paketlemenin bir yolunu gösteriyor:

def has_elements(iter):
  from itertools import tee
  iter, any_check = tee(iter)
  try:
    any_check.next()
    return True, iter
  except StopIteration:
    return False, iter

has_el, iter = has_elements(iter)
if has_el:
  # not empty

Bu gerçekten pitonik değildir ve belirli durumlar için, bir sonraki varsayılan gibi muhtemelen daha iyi (ancak daha az genel) çözümler vardır .

first = next(iter, None)
if first:
  # Do something

Bu genel değildir çünkü Hiçbiri birçok yinelemede geçerli bir öğe olabilir.


Muhtemelen bunu yapmanın en iyi yolu budur. Ancak, OP'nin ne yapmaya çalıştığını bilmek yardımcı olur mu? Muhtemelen daha zarif bir çözüm var (sonuçta bu IS Python).
rossipedia

Teşekkürler, sanırım kullanacağım next().
Bastien Léonard

1
@ Bastien, peki, ama bunu uygun bir gözcü ile yap ( cevabıma bakın).
Alex Martelli

3
Bu çözümde büyük bir bellek sızıntısı var. teeİtertools içinde durumunda orijinal İlerleticiden her eleman tutmak zorunda kalacak any_checkönceden hiç ihtiyaçları. Bu, orijinal yineleyiciyi bir listeye dönüştürmekten daha kötüdür.
Rafał Dowgird

1
@ RafałDowgird Bu, orijinal yineleyiciyi bir listeye dönüştürmekten daha kötü. Pek sayılmaz - sonsuz dizileri düşünün.
Piotr Dobrogost

6

kullanabilirsiniz:

if zip([None], iterator):
    # ...
else:
    # ...

ama kod okuyucu için biraz açıklayıcı değil


2
.. ([Yok] yerine herhangi bir tek öğeli yinelenebilir kullanabilirsiniz)
mykhal

5

Bunu yapmanın en iyi yolu, bir ile peekablegelen more_itertools.

from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
    # Iterator is non-empty.
else:
    # Iterator is empty.

Sadece eski yineleyiciye refs tutarsanız dikkatli olun, bu yineleyici ilerleyecektir. O andan itibaren yeni peekable yineleyiciyi kullanmalısınız. Gerçekten, yine de, peekable, o eski yineleyiciyi değiştiren tek kod parçası olmayı bekler, bu nedenle yine de ortalıkta yatan eski yineleyicinin referanslarını tutmamalısınız.


3

Ne dersin:

In [1]: i=iter([])

In [2]: bool(next(i,False))
Out[2]: False

In [3]: i=iter([1])

In [4]: bool(next(i,False))
Out[4]: True

4
İlginç biri! Ama ya next () False değerini döndürürse, çünkü gerçekten elde edilen budur?
Bastien Léonard

@ BastienLéonard Bir sınıf oluşturun class NotSet: pass, sonra kontrol edinif next(i, NotSet) is NotSet: print("Iterator is empty")
Elijas Dapšauskas

-1

__length_hint__ uzunluğunu tahmin eder list(it)- yine de özel bir yöntemdir:

x = iter( (1, 2, 3) )
help(x.__length_hint__)
      1 Help on built-in function __length_hint__:
      2 
      3 __length_hint__(...)
      4     Private method returning an estimate of len(list(it)).

4
her yineleyici için garanti edilmez. >>> def it (): ... verim 1 ... verim 2 ... verim 3 ... >>> i = it () >>> i .__ uzunluk_hint__ Geri izleme (en son çağrı): Dosya " <stdin> ", satır 1, <module> içinde AttributeError: 'generator' nesnesinin ' length_hint ' özelliği yok
andrewrk

3
Sıfırdan fazla girişi olan bir yineleyici için 0 döndürmesi muhtemelen yasaldır, çünkü bu sadece bir ipucu.
Glenn Maynard

-1

Bu, genellikle bir sonraki öğe olup olmadığını (boolean'a dönüştürme yoluyla) kontrol etmeye izin veren bir aşırı öldürme yineleyici sarmalayıcısıdır. Tabii ki oldukça verimsiz.

class LookaheadIterator ():

    def __init__(self, iterator):
        self.__iterator = iterator
        try:
            self.__next      = next (iterator)
            self.__have_next = True
        except StopIteration:
            self.__have_next = False

    def __iter__(self):
        return self

    def next (self):
        if self.__have_next:
            result = self.__next
            try:
                self.__next      = next (self.__iterator)
                self.__have_next = True
            except StopIteration:
                self.__have_next = False

            return result

        else:
            raise StopIteration

    def __nonzero__(self):
        return self.__have_next

x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)

x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)

Çıktı:

False
[]
True
[1, 2, 3]

-2

Biraz geç, ama ... Yineleyiciyi bir listeye dönüştürebilir ve ardından bu listeyle çalışabilirsiniz:

# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]

# If the list is not empty then the iterator had elements; else it was empty.
if l :
    pass # Use the elements of the list (i.e. from the iterator)
else :
    pass # Iterator was empty, thus list is empty.

4
Bu verimsizdir çünkü tüm listeyi numaralandırır. Sonsuz jeneratörler için çalışmaz.
becko

@becko: Kabul edildi. Ancak asıl soruda durum böyle görünmüyor.
Jens

3
Diğer bir sorun da yineleyicinin sonsuz sayıda nesne üretebilmesidir, bu da bellek taşmasına neden olabilir ve programın bir sonraki ifadeye asla ulaşamayacağı
gerçeğidir
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.