Pertanyaan Apakah Pythonic menggunakan list comprehensions hanya untuk efek samping?


Pikirkan tentang fungsi yang saya panggil untuk efek sampingnya, bukan mengembalikan nilai (seperti mencetak ke layar, memperbarui GUI, mencetak ke file, dll.).

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

Sekarang, benar Pythonic untuk menggunakan pemahaman daftar untuk memanggil fungsi ini:

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

Perhatikan bahwa saya tidak menyimpan daftar di mana saja

Atau haruskah saya memanggil func seperti ini:

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

Mana yang lebih baik dan mengapa?


75
2018-04-22 08:22


asal


Jawaban:


Ini sangat anti-Pythonic untuk melakukannya, dan setiap Pythonista berpengalaman akan memberi Anda neraka di atasnya. Daftar antara dibuang setelah dibuat, dan berpotensi sangat, sangat besar, dan karena itu mahal untuk dibuat.


61
2018-04-22 08:24



Anda tidak seharusnya menggunakan daftar pemahaman, karena seperti yang orang katakan itu akan membangun daftar sementara besar yang tidak Anda perlukan. Dua metode berikut ini setara:

consume(side_effects(x) for x in xs)

for x in xs:
    side_effects(x)

dengan definisi consume dari itertools halaman manual:

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)

Tentu saja, yang terakhir lebih jelas dan mudah dimengerti.


27
2018-04-22 08:38



List comprehensions adalah untuk membuat daftar. Dan kecuali Anda benar-benar membuat daftar, Anda harus tidak menggunakan pemahaman daftar.

Jadi saya akan mendapatkan opsi kedua, hanya mengulang daftar dan kemudian memanggil fungsi ketika ketentuan berlaku.


20
2018-04-22 08:27



Kedua lebih baik.

Pikirkan orang yang perlu memahami kode Anda. Anda bisa mendapatkan karma buruk dengan mudah dengan yang pertama :)

Anda bisa pergi ke tengah di antara keduanya dengan menggunakan filter (). Pertimbangkan contohnya:

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)

9
2018-04-22 08:30



Tergantung pada tujuan Anda.

Jika Anda mencoba melakukan beberapa operasi pada setiap objek dalam daftar, pendekatan kedua harus diadopsi.

Jika Anda mencoba membuat daftar dari daftar lain, Anda dapat menggunakan pemahaman daftar.

Eksplisit lebih baik daripada implisit.   Sederhana lebih baik daripada kompleks. (Python Zen)


2
2018-04-22 08:30



Anda dapat melakukan

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

tapi itu tidak terlalu cantik.


1
2018-04-22 08:33



Saya akan melakukan ini sebagai gantinya:

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

Ini adalah ekspresi generator, dan tidak menghasilkan daftar acak yang terlempar keluar. Saya pikir itu jelek dan saya tidak akan benar-benar melakukannya dalam kode. Tetapi jika Anda bersikeras menerapkan loop Anda dengan cara ini, itulah cara saya melakukannya.

Saya cenderung merasa bahwa daftar pemahaman dan sejenisnya akan menandakan upaya untuk menggunakan sesuatu yang setidaknya samar-samar menyerupai gaya fungsional. Menempatkan sesuatu dengan efek samping yang merusak asumsi itu akan menyebabkan orang harus membaca kode Anda lebih hati-hati, dan saya pikir itu hal yang buruk.


0
2018-04-22 08:38