FOR döngüsü ve IF deyimini birleştirmenin pythonic yolu


266

Hem döngüler için hem de ayrı satırlardaki ifadeler için nasıl kullanılacağını biliyorum:

>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
...     if x in a:
...         print(x)
0,4,6,7,9

Ve ifadeler basit olduğunda bunları birleştirmek için bir liste kavrayışı kullanabileceğimi biliyorum, örneğin:

print([x for x in xyz if x in a])

Ama ne bulamıyorum iyi bir örnek (kopyalamak ve öğrenmek için) bir for döngüsü ve bazı if deyimleri birleşimi sonrasında oluşan karmaşık komutlar kümesi (sadece "print x" değil) gösteren. Beklediğim bir şey şöyle:

for x in xyz if x not in a:
    print(x...)

Bu sadece pitonun çalışması gerektiği gibi değil mi?


23
İşte böyle ... şeyleri basitleştirmeye çalışarak aşırı karmaşıklaştırma. Pythonic her açık fordöngü ve ififadeden kaçınmak anlamına gelmez .
Felix Kling

2
Liste kavrayışınızda oluşturulan listeyi bir for döngüsünde kullanabilirsiniz. Bu son örneğiniz gibi görünüyor.
Jacob

Bu nedenle, işlemeye başlayarak, if ifadesi zaten eşleştirilen değerleri hariç tutuyorsa ve for döngüsünün yinelemesi sırasında liste sürekli büyüyorsa, bir for döngüsünü bir if ifadesiyle birleştirmenin en hızlı yolu nedir?
ChewyChunks

3
@Chewy, uygun veri yapıları, kodu sözdizimsel şeker değil, daha hızlı hale getirecektir. Örneğin , bir liste x in aise yavaştır a.
Nick Dandoulakis

1
Bu, yorumlanmış bir dil olan Python; Neden hiç kimse kodun ne kadar hızlı olduğunu tartışıyor?
ArtOfWarfare

Yanıtlar:


323

Bunun gibi jeneratör ifadelerini kullanabilirsiniz :

gen = (x for x in xyz if x not in a)

for x in gen:
    print x

1
gen = (y for (x,y) in enumerate(xyz) if x not in a)12yazarken >>> döndürür for x in gen: print x- neden numaralandırma ile beklenmedik davranış?
ChewyChunks

9
Mümkün, ancak bloklar için ve if orijinalinden daha güzel değil.
Mike Graham

1
@ChewyChunks. Bu işe yarayacaktır ancak numaralandırma çağrısı gereksizdir.
Johnsyweb

132
Gerçekten python diyebilirim özledimfor x in xyz if x:
bgusach

10
for x in (x for x in xyz if x not in a):benim için çalışıyor, ama neden sadece yapamaman gerekiyor for x in xyz if x not in a:, emin değilim ...
Matt Wenham

34

Gereğince Python Zen (eğer kod, bu gidilecek yer var "Pythonic" olup olmadığını merak varsa):

  • Güzel, çirkin olmaktan iyidir.
  • Açık, örtük olmaktan iyidir.
  • Basit, karmaşık olmaktan iyidir.
  • Düz iç içe geçmişten daha iyidir.
  • Okunabilirlik önemlidir.

İki s'nin Pythonic yolu :sorted intersectionset

>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]

Veya içinde olmayan xyzama içinde olmayan unsurlar a:

>>> sorted(set(xyz).difference(a))
[12, 242]

Ancak daha karmaşık bir döngü için, iyi adlandırılmış bir üreteç ifadesini yineleyerek ve / veya iyi adlandırılmış bir işleve çağrı yaparak düzleştirmek isteyebilirsiniz . Her şeyi tek bir satıra sığdırmaya çalışmak nadiren "Pitonik" tir.


Sorunuzla ilgili ek yorumları ve kabul edilen cevabı izleyerek güncelleyin

Ne yapmaya çalıştığınızdan emin değilim enumerate, ancak abir sözlükse, tuşları şu şekilde kullanmak istersiniz:

>>> a = {
...     2: 'Turtle Doves',
...     3: 'French Hens',
...     4: 'Colly Birds',
...     5: 'Gold Rings',
...     6: 'Geese-a-Laying',
...     7: 'Swans-a-Swimming',
...     8: 'Maids-a-Milking',
...     9: 'Ladies Dancing',
...     0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
...     print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
...     print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas

Aşağıdaki yorumlardan olduğu gibi, jeneratörler üzerinde çalışmalıyım. Onları hiç kullanmadım. Teşekkürler. Bir jeneratör FOR ve IF ifadelerinin eşdeğer kombinasyonundan daha mı hızlı? Ayrıca kümeler kullandım, ancak bazen bir listedeki gereksiz öğeler atamadığım bilgilerdir.
ChewyChunks

@ChewyChunks: Pythonic olmanın tek yolu jeneratörler değil!
Johnsyweb

3
@Johnsyweb, Python'un Zen'inden alıntı yapacaksanız: "Bir tane olmalı - ve tercihen bir tane olmalı - bunu yapmanın açık bir yolu."
Wooble

@Wooble: Olmalı. Bu bölümü , aynı zamanda başka bir soruya cevabımda alıntıladım !
Johnsyweb

18

Şahsen bu en güzel sürüm olduğunu düşünüyorum:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
  print x

Düzenle

lambda kullanmaktan kaçınmaya çok hevesliyseniz, kısmi fonksiyon uygulamasını kullanabilir ve operatör modülünü kullanabilirsiniz (çoğu operatörün fonksiyonlarını sağlar).

https://docs.python.org/2/library/operator.html#module-operator

from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))

4
filter(a.__contains__, xyz). Genellikle insanlar lambda kullandıklarında, gerçekten daha basit bir şeye ihtiyaç duyarlar.
Veky

Bence bir şeyi yanlış anladın. __contains__diğeri gibi bir yöntemdir, sadece özel bir yöntemdir, yani bir operatör tarafından dolaylı olarak çağrılabilir ( inbu durumda). Ancak doğrudan da çağrılabilir, genel API'nin bir parçasıdır. Özel adlar, özel yöntem adları için istisna sağlamak üzere özel olarak en fazla bir alt çizgiye sahip olarak tanımlanır ve sınıf kapsamlarında sözcüksel olarak söz konusu olduğunda ad yönetimine tabidir. Bkz. Docs.python.org/3/reference/datamodel.html#specialnames ve docs.python.org/3.6/tutorial/classes.html#private-variables .
Veky

Kesinlikle tamam, ama sadece bir özellik kullanarak erişilebilir bir yönteme başvurabilmek için iki ithalat garip görünüyor (operatörler genellikle çift gönderim gerektiğinde kullanılır, ancak insağ işlenen için tek başına gönderilir). Ayrıca, adı altında yöntemi operatorde ihraç ettiğine dikkat edin , bu yüzden kesinlikle özel bir isim değildir. Her çift alt çizginin "uzak dur" anlamına gelmediği gerçeğiyle yaşamayı öğrenmeniz gerektiğini düşünüyorum. : -]contains__contains__
Veky

Ben lambdadahil olmak üzere sabitleme ihtiyaçlarınızı düşünüyorum not: lambda w: not w in a, xyz
javadba

Filtre, özellikle lambdalar yerine tanımlanmış işlevler haline gelebilecek karmaşık koşullar için daha zarif görünüyor, belki lambda işlevini adlandırmak biraz okunabilirlik ekleyecek, yinelenen öğeler liste öğelerinde bir değişiklik olduğunda jeneratör daha iyi görünüyor
Khanis Rok

16

Aşağıdaki, kabul edilen cevaptan bir basitleştirme / bir astardır:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]

for x in (x for x in xyz if x not in a):
    print(x)

12
242

Satır içigenerator tutulduğuna dikkat edin . Bu test edildi ve ((parens ;;)python2.7python3.6print


10

Muhtemelen kullanacağım:

for x in xyz: 
    if x not in a:
        print x...

@KirillTitov Evet python temel olarak işlevsel olmayan bir dildir (bu tamamen zorunlu bir kodlamadır - ve bu cevabın yazarı ile python'un yazılması için ayarlandığı yol olduğunu kabul ediyorum. pythonicİşlevsel olarak kullandığım her dilde kodlama yapabilirim (scala, kotlin, javascript, R, swift, ..) ama
python'da

9
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]  
set(a) & set(xyz)  
set([0, 9, 4, 6, 7])

Çok Zen, @ lazyr, ancak bir liste üzerinden yineleme yapmaya ve başka bir listedeki eşleşen öğeleri yok saymaya bağlı karmaşık bir kod bloğunu geliştirmeme yardımcı olmaz. İlk listeyi küme olarak ele almak ve birliği / farkı ikinci büyüyen "yoksay" listesiyle karşılaştırmak daha mı hızlı?
ChewyChunks

Bunu deneyinimport time a = [2,3,4,5,6,7,8,9,0] xyz = [0,12,4,6,242,7,9] start = time.time() print (set(a) & set(xyz)) print time.time() - start
Kracekumar

@ChewyChunks, yineleme sırasında listelerden herhangi biri değişirse, her öğeyi yok sayma listesine göre kontrol etmek daha hızlı olacaktır - ancak bir yok sayma kümesi yapmanız gerekir. Setlerinde üyelik için denetleniyor çok hızlıdır: if x in ignore: ....
Lauritz V. Thaulow

@ lazyr Sadece yoksay listesi üzerinde ayarlanmış bir yoksay kullanarak kodumu yeniden yazdım . Süre çok daha yavaş işliyor gibi görünüyor. (Adil olmak gerekirse bunu kullanarak karşılaştırıyordum if set(a) - set(ignore) == set([]):belki de bu yüzden üyeliği kontrol etmekten çok daha yavaştı. Bunu gelecekte yazdığımdan çok daha basit bir örnekte test edeceğim.
ChewyChunks

5

Sen kullanabilirsiniz jeneratörler jeneratör ifadeleri çok karıştı ya da karmaşık hale gelirse, çok:

def gen():
    for x in xyz:
        if x in a:
            yield x

for x in gen():
    print x

Bu benim için biraz daha yararlı. Hiç jeneratörlere bakmadım. Korkutucu geliyorlar (çünkü onları genellikle kullanmak için acı olan modüllerde gördüm).
ChewyChunks

2

intersectionVeya kullanınintersection_update

  • kavşak :

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    ans = sorted(set(a).intersection(set(xyz)))
  • kesişim_tarihi :

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    b = set(a)
    b.intersection_update(xyz)

    o zaman bcevabın


2

Ben sevdim Alex'in cevabı bir nedeni, filtre tam bir olup olmadığını bir liste uygulanan böylece bir koşul verilen bir listenin bir alt kümesini keşfetmek istiyorsanız, bu en doğal yolu olarak görünmektedir,

mylist = [1,2,3,4,5]
another_list = [2,3,4]

wanted = lambda x:x in another_list

for x in filter(wanted, mylist):
    print(x)

bu yöntem endişelerin ayrılması için yararlıdır, eğer koşul fonksiyonu değişirse, kemanlanacak tek kod işlevin kendisidir.

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

for x in filter(wanted, mylist):
    print(x)

Jeneratör yöntem listenin üyelerini istemediğinizde daha iyi görünüyor, ama bir daha oturmasını gibi görünmesine karşın, üyelerin değiştirilmesi, jeneratör

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.5 for x in mylist if wanted(x))

for x in generator:
    print(x)

Ayrıca, filtreler jeneratörlerle çalışır, ancak bu durumda verimli değildir

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

generator = (x**0.9 for x in mylist)

for x in filter(wanted, generator):
    print(x)

Ama elbette, böyle yazmak hala güzel olurdu:

mylist = [1,2,3,4,5]

wanted = lambda x:(x**0.5) > 10**0.3

# for x in filter(wanted, mylist):
for x in mylist if wanted(x):
    print(x)

0

A ve b listelerinin benzersiz ortak öğelerini bulmanın basit bir yolu:

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)
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.