Listedeki tüm öğelerin bir koşulla eşleşip eşleşmediğini nasıl kontrol edebilirim?


208

20000 listeden oluşan bir listem var. Her listenin 3. öğesini bayrak olarak kullanıyorum. En az bir öğenin bayrağı 0 olduğu sürece bu listede bazı işlemler yapmak istiyorum, şöyle:

my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]

Başlangıçta, tüm bayraklar 0'dır. En az bir elemanın bayrağının 0 olup olmadığını kontrol etmek için while döngüsü kullanıyorum:

def check(list_):
    for item in list_:
        if item[2] == 0:
            return True
    return False

check(my_list)İade olursa True, listemde çalışmaya devam ediyorum:

while check(my_list):
    for item in my_list:
        if condition:
            item[2] = 1
        else:
            do_sth()

Aslında, yinelediğim gibi my_list öğesindeki bir öğeyi kaldırmak istedim, ancak yinelediğimde öğeleri kaldırmam yasak.

Orijinal listemde bayrak yoktu:

my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]

Üzerinde yineleme yaptığım için elemanları kaldıramadığım için bu bayrakları icat ettim. Ancak my_listbirçok öğe içerir ve whiledöngü her fordöngüde hepsini okur ve çok zaman alır! Önerin var mı?


3
Veri yapınız probleminiz için ideal değil gibi görünüyor. Bağlamı biraz daha açıkladıysanız belki daha uygun bir şey önerebiliriz.
uselpa

Belki de öğeleri listeden çıkarmak yerine listeyle Noneya []da tekrarlayarak değiştirebilirsiniz . Tüm listeyi iç döngüdeki her geçişten önce tüm öğeleri yineleyen 'check () `ile kontrol etmek çok yavaş bir yaklaşımdır.
martineau

Yanıtlar:


402

Buradaki en iyi cevap all(), bu durum için yerleşik olan kullanmaktır . İstediğiniz sonucu temiz ve verimli bir şekilde üretmek için bunu bir jeneratör ifadesiyle birleştiriyoruz . Örneğin:

>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False

all(flag == 0 for (_, _, flag) in items)Doğrudan buna eşdeğer olduğunu unutmayın, all(item[2] == 0 for item in items)bu durumda okumak sadece biraz daha hoş.

Ve filtre örneği için, bir liste kavrama (elbette, uygun olduğunda bir jeneratör ifadesi kullanabilirsiniz):

>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]

En az bir öğenin 0 olup olmadığını kontrol etmek istiyorsanız, daha iyi olan seçenek any()daha okunabilir olanı kullanmaktır :

>>> any(flag == 0 for (_, _, flag) in items)
True

Lambda kullanımıyla ilgili suçum, Python'un hepsi Haskell et gibi ilk argüman olarak bir işlevi kabul etmiyor. Ayrıca, cevabımı liste kavrayışına da değiştirdim. :)
Hampus Nilsson

3
@HampusNilsson Liste kavraması, bir jeneratör ifadesiyle aynı değildir. Olarak all()ve any()kısa devre, örneğin, benimki ilk değer olarak değerlendirilirse False, all()başarısız olur ve daha fazla değer kontrol etmez, geri döner False. Örneğiniz aynısını yapacaktır, ancak önce karşılaştırma listesinin tamamını oluşturacaktır, yani hiçbir şey için çok fazla işlem gerçekleştirilecektir.
Gareth Latty

14

Listedeki herhangi bir öğenin koşul kullanımını ihlal edip etmediğini kontrol etmek istiyorsanız all:

if all([x[2] == 0 for x in lista]):
    # Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)

Eşleşmeyen tüm öğeleri kaldırmak için şunu kullanın: filter

# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)

2
Sen kaldırabilirsiniz [...]içinde all(...)o zaman bunun yerine sadece size iki karakter kazandırır listesi, bir jeneratör oluşturabilir beri değil, aynı zamanda bellek ve zaman kazandırır. Jeneratörler kullanılarak, bir seferde yalnızca bir öğe hesaplanacaktır (artık kullanılmadığından önceki sonuçlar bırakılacaktır) ve bunlardan herhangi biri ortaya çıkarsa False, jeneratör geri kalanını hesaplamayı durduracaktır.
InQβ

7

İtertools'un takewhile'ını böyle kullanabilirsiniz, ifadeniz başarısız olan bir koşul karşılandığında durur. Bunun tersi yöntem açılır

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x

0

Kullanmanın başka bir yolu itertools.ifilter. Bu doğruluk ve süreci kontrol eder (kullanarak lambda)

Örneklem-

for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
    print x

0

bu şekilde kullanmaktan biraz daha esnektir all():

my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False

veya daha özlü bir şekilde:

all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]

Basitçe söyleyemez misin, all_zeros = False in [x[2] == 0 for x in my_list]hatta 0 in [x[2] for x in my_list]buna karşılık gelebilir any_zerosmisin? Gerçekten dikkate değer bir gelişme görmüyorum all().
tripleee

Hayır, sürümünüz - olarak all_zeros = False in [x[2] == 0 for x in my_list]değerlendirilir False, benimki ise değerlendirir True. Bunu olarak değiştirirseniz, bu all_zeros = not (False in [x[2] == 0 for x in my_list])benimkine eşdeğerdir. Ve 0 in [x[2] for x in my_list]belli ki sadece işe yarayacak any_zeros. Ama fikrinin
özünü seviyorum
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.