Pythonic sadece yan etkiler için liste anlamalarını kullanmak mı?


108

Yan etkileri için çağırdığım bir işlevi düşünün, değerleri döndürmeyin (ekrana yazdırma, GUI'yi güncelleme, bir dosyaya yazdırma vb.).

def fun_with_side_effects(x):
    ...side effects...
    return y

Şimdi, bu işlevi çağırmak için liste anlamalarını kullanmak Pythonic mi?

[fun_with_side_effects(x) for x in y if (...conditions...)]

Listeyi hiçbir yere kaydetmediğimi unutmayın

Yoksa bu func şöyle mi demeliyim:

for x in y:
    if (...conditions...):
        fun_with_side_effects(x)

Hangisi daha iyi ve neden?


6
bu sınırda, ancak muhtemelen destekten daha fazla karşı çıkacaksınız. Bunu dışarıda
tutacağım

6
Bu kolay bir seçimdir. Okunabilirlik önemlidir - bunu ikinci yoldan yapın. Ekranınıza fazladan 2 çizgi
sığamazsanız

1
Liste anlama, "açık, örtükten daha iyidir" i ihlal ettiği için pirtonik değildir - bir döngüyü farklı bir yapıda saklıyorsunuz.
Fred Foo

3
@larsmans: Keşke GvR, liste anlayışlarını ilk sırada tanıttığında bunu fark etseydi!
Steve Jessop

2
@larsmans, Steve Jessop, bir liste anlayışını bir döngü olarak düşünmenin yanlış olduğunu düşünüyorum. Bir döngü olarak uygulanabilir, ancak bunun gibi yapıların amacı, birleşik veriler üzerinde işlevsel ve (kavramsal olarak) paralel bir şekilde çalışmaktır. Sözdizimiyle ilgili bir sorun varsa, bu for ... inher iki durumda da kullanılır - buna benzer sorulara yol açar!
gönderen

Yanıtlar:


84

Bunu yapmak çok anti-Pythonic ve tecrübeli herhangi bir Pythonista sizi cehenneme çevirecektir. Ara liste, oluşturulduktan sonra atılır ve potansiyel olarak çok, çok büyük ve bu nedenle oluşturulması pahalı olabilir.


5
Öyleyse daha pitonik bir yol ne olabilir?
Joachim Sauer

6
Listeyi etrafta tutmayan; yani, ikinci yolun bir varyantı (daha forönce, ondan kurtulmak için bir geneks kullandığım bilinmektedir if).
Ignacio Vazquez-Abrams

6
@Joachim Sauer: Yukarıdaki Örnek 2. Düzgün, açık, liste-anlama dışı bir döngü. Açık. Açık. Açık.
S.Lott

31

Bir liste anlama kullanmamalısınız , çünkü insanların söylediği gibi, ihtiyacınız olmayan büyük bir geçici liste oluşturacaktır. Aşağıdaki iki yöntem eşdeğerdir:

consume(side_effects(x) for x in xs)

for x in xs:
    side_effects(x)

tanımından consumegelen itertoolsadam sayfası:

def consume(iterator, n=None):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

Tabii ki, ikincisi daha net ve anlaşılması daha kolay.


@Paul: Bence olmalı. Ve gerçekten de yapabilirsiniz, ancak mapdaha önce işlevsel programlama yapmadıysanız, sezgisel olmayabilirsiniz.
Katriel

4
Bunun özellikle deyimsel olduğundan emin değilim. Açık döngüyü kullanmanın hiçbir avantajı yoktur.
Marcin

1
Çözümconsume = collections.deque(maxlen=0).extend
PaulMcG

24

Liste anlayışları, listeler oluşturmak içindir. Aslında bir listesini oluşturarak sürece, sen gerektiğini değil Liste comprehensions kullanın.

Bu yüzden ikinci seçeneği elde ettim, sadece listeyi yineleyerek ve ardından koşullar uygulandığında işlevi çağırırdım.


6
Daha da ileri gidip, işiniz bittiğinde ortaya çıkan listeyi kullanıyor olsanız bile, bir liste anlayışındaki yan etkilerin olağandışı, beklenmedik ve bu nedenle kötü olduğunu söylerim.
Mark Ransom

11

İkincisi daha iyi.

Kodunuzu anlaması gereken kişiyi düşünün. İlkinde kolayca kötü karmaya kavuşabilirsiniz :)

Filter () kullanarak ikisinin ortasına gidebilirsiniz. Örneği düşünün:

y=[1,2,3,4,5,6]
def func(x):
    print "call with %r"%x

for x in filter(lambda x: x>3, y):
    func(x)

10
Lambda'nız çok daha iyi yazılır lambda x : x > 3.
PaulMcG

Filtreye bile ihtiyacınız yok. Sadece burada Pars bir jeneratör ifadesini koyun: for el in (x for x in y if x > 3):. elve xaynı ada sahip olabilir, ancak bu insanların kafasını karıştırabilir.
Omnifarious

3

Hedefinize bağlıdır.

Listedeki her nesne üzerinde bir işlem yapmaya çalışıyorsanız, ikinci yaklaşım benimsenmelidir.

Başka bir listeden bir liste oluşturmaya çalışıyorsanız, liste anlamayı kullanabilirsiniz.

Açık, örtük olmaktan daha iyidir. Basit, karmaşıktan daha iyidir. (Python Zen)


0

Yapabilirsin

for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass

ama çok hoş değil.


-1

Yan etkileri için bir liste anlayışı kullanmak çirkin, Pythonic değildir, verimsizdir ve bunu yapmam. Bunun foryerine bir döngü kullanırdım, çünkü bir fordöngü yan etkilerin önemli olduğu prosedürel bir tarzı işaret eder.

Ancak, yan etkileri için bir liste anlamayı kullanmakta kesinlikle ısrar ediyorsanız, bunun yerine bir üreteç ifadesi kullanarak verimsizlikten kaçınmalısınız. Bu stilde kesinlikle ısrar ediyorsanız, şu ikisinden birini yapın:

any(fun_with_side_effects(x) and False for x in y if (...conditions...))

veya:

all(fun_with_side_effects(x) or True for x in y if (...conditions...))

Bunlar üretici ifadeleridir ve fırlatılıp atılan rastgele bir liste oluşturmazlar. Benceall form biraz daha net, ancak ikisinin de kafa karıştırıcı olduğunu ve kullanılmaması gerektiğini düşünüyorum.

Bunun çirkin olduğunu düşünüyorum ve bunu kodla yapmam. Ancak döngülerinizi bu şekilde uygulamakta ısrar ediyorsanız, ben böyle yapardım.

Liste anlayışlarının ve benzerlerinin, en azından hafif bir şekilde işlevsel bir stile benzeyen bir şeyi kullanma girişimine işaret etmesi gerektiğini hissediyorum. Bu varsayımı bozan yan etkileri olan şeyler koymak, insanların kodunuzu daha dikkatli okumasına neden olur ve bence bu kötü bir şey.


fun_with_side_effectsTrue döndürürse ne olur ?
Katriel

7
Bence bu tedavi hastalıktan daha kötü - itertools. Tüketmek çok daha temiz.
PaulMcG

@PaulMcG - itertools.consumeartık mevcut değil, çünkü muhtemelen yan etkileri olan anlamaları kullanmak çirkin.
Omnifarious

1
Görünüşe göre yanılmışım ve stdlib'de hiçbir zaman bir yöntem olarak var olmadı . Bu ise itertools docs bir reçete: docs.python.org/3/library/...
PaulMcG
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.