Cara menggunakan filter, memetakan, dan mengurangi dengan Python 3

321

filter, map, Dan reducebekerja sempurna di Python 2. Berikut adalah contoh:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

Namun dalam Python 3, saya menerima output berikut:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

Saya akan sangat menghargai jika seseorang dapat menjelaskan kepada saya mengapa ini terjadi.

Tangkapan layar kode untuk kejelasan lebih lanjut:

Sesi IDLE dari Python 2 dan 3 berdampingan

Dick Lucas
sumber
1
Singkatnya, daftar bukan satu-satunya tipe data. Jika Anda ingin daftar, katakan Anda ingin daftar. Tetapi dalam kebanyakan kasus, Anda tetap menginginkan sesuatu yang lain.
Veky

Jawaban:

346

Anda dapat membaca tentang perubahan pada What's New In Python 3.0 . Anda harus membacanya dengan seksama ketika Anda berpindah dari 2.x ke 3.x karena banyak yang telah diubah.

Seluruh jawaban di sini adalah kutipan dari dokumentasi.

Tampilan Dan Iterator Alih-alih Daftar

Beberapa API terkenal tidak lagi menampilkan daftar:

  • [...]
  • map()dan filter()mengembalikan iterators. Jika Anda benar-benar membutuhkan daftar, perbaikan cepat misalnya list(map(...)), tetapi perbaikan yang lebih baik sering menggunakan pemahaman daftar (terutama ketika kode asli menggunakan lambda), atau menulis ulang kode sehingga tidak memerlukan daftar sama sekali. Terutama rumit map()diminta untuk efek samping dari fungsi; transformasi yang benar adalah dengan menggunakan forloop reguler (karena membuat daftar hanya akan sia-sia).
  • [...]

Dibangun

  • [...]
  • Dihapus reduce(). Gunakan functools.reduce()jika Anda benar-benar membutuhkannya; namun, 99 persen dari waktu forperulangan eksplisit lebih mudah dibaca.
  • [...]
nhahtdh
sumber
21
Menambahkan di list(map(...) mana-mana .. bagaimana dunia membantu keterbacaan .. pythonsepertinya tidak dapat menangani aplikasi progresif / streaming dari kombinator fungsional. Bahasa lain saya dapat membuat rantai selusin operasi terhadap koleksi berturut-turut dan itu dapat dibaca. Sini? apa yang Anda inginkan - selusin cara bersarang in??
javadba
11
Jika Anda bekerja dalam konteks imperatif, maka for-loop mungkin merupakan opsi yang lebih mudah dibaca. Tetapi ada alasan bagus untuk memilih konteks fungsional - dan memutuskan dari situ untuk kembali ke prosedural bisa sangat buruk.
MatrixManAtYrService
2
@javadba Anda yakin di "aplikasi streaming" Anda perlu menambahkan listpanggilan sama sekali? Saya pikir arti "streaming" adalah "tidak ada daftar sama sekali; proses setiap elemen dari input sepenuhnya sebelum pindah ke yang berikutnya".
Imperishable Night
@ MatrixManAtYrService Jika Anda yakin perilaku python 2 adalah yang Anda butuhkan, Anda selalu bisa mendefinisikan ulang map.
Imperishable Night
6
Saya masih tidak mengerti bagaimana argumen keterbacaan mengarah pada perubahan seperti itu. Jika itu untuk alasan kinerja saya mungkin mengerti ...
Minato
86

Fungsionalitas mapdan filtersengaja diubah untuk mengembalikan iterator, dan mengurangi dihapus dari menjadi built-in dan ditempatkan di functools.reduce.

Jadi, untuk filterdan map, Anda dapat membungkusnya dengan list()untuk melihat hasil seperti yang Anda lakukan sebelumnya.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

Rekomendasi sekarang adalah bahwa Anda mengganti penggunaan peta dan filter dengan ekspresi generator atau pemahaman daftar. Contoh:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

Mereka mengatakan bahwa untuk loop adalah 99 persen dari waktu lebih mudah dibaca daripada mengurangi, tapi saya hanya akan bertahan functools.reduce.

Sunting : Angka 99 persen ditarik langsung dari halaman What's New In Python 3.0 yang ditulis oleh Guido van Rossum.

Joshua D. Boyd
sumber
5
Anda tidak perlu membuat fungsi tambahan dalam memahami daftar. Cukup gunakan[i*i*i for i in range(1,11)]
Xiao
2
Anda benar sekali. Saya menyimpan fungsi dalam contoh-contoh pemahaman daftar agar tetap terlihat mirip dengan contoh filter / peta.
Joshua D. Boyd
5
i ** 3 juga setara dengan i * i * i
Breezer
5
@Breezer sebenarnya i**3akan memanggil i.__pow__(3)dan i*i*i i.__mul__(i).__mul__(i)(atau sesuatu seperti itu). Dengan ints itu tidak masalah tetapi dengan angka numpy / kelas khusus bahkan mungkin menghasilkan hasil yang berbeda.
syntonym
1
Saya perhatikan bahwa setiap kali kita mendengar bahwa "Guido membuat keputusan X" bahwa rasa sakit adalah kemungkinan hasil. Ini adalah contoh yang bagus: list(list(list(.. )))untuk melakukan apa yang sudah verbose dengan python.
javadba
12

Sebagai tambahan untuk jawaban lain, ini kedengarannya seperti kasus penggunaan yang baik untuk manajer konteks yang akan memetakan kembali nama-nama fungsi ini ke yang mengembalikan daftar dan memperkenalkan reducedi namespace global.

Implementasi cepat mungkin terlihat seperti ini:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

Dengan penggunaan yang terlihat seperti ini:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

Yang mencetak:

190
[1, 2]

Hanya 2 sen saya :-)

Dimitris Fasarakis Hilliard
sumber
1
pythonsebagai bahasa berantakan - tetapi memiliki v baik untuk perpustakaan yang sangat baik: numpy, pandas, statsmodelsdan teman-teman .. Saya telah buliding perpustakaan kenyamanan seperti Anda menunjukkan di sini untuk mengurangi rasa sakit dari bahasa asli - tetapi telah kehilangan energi dan mencoba untuk tidak menyimpang jauh dari a data.frame/ datatable, atau xarray. Tapi pujian untuk mencoba ..
javadba
7

Karena reducemetode ini telah dihapus dari fungsi bawaan dari Python3, jangan lupa untuk mengimpor functoolsdalam kode Anda. Silakan lihat potongan kode di bawah ini.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)
Bikash Singh
sumber
2

Berikut adalah contoh-contoh dari Filter, map dan pengurangan fungsi

angka = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

//Saring

oddNumbers = daftar (filter (lambda x: x% 2! = 0, angka))

print (oddNumbers)

//Peta

multiplyOf2 = daftar (peta (lambda x: x * 2, angka))

cetak (multiplyOf2)

//Mengurangi

Fungsi pengurangan, karena tidak umum digunakan, telah dihapus dari fungsi bawaan di Python 3. Masih tersedia di modul functools, sehingga Anda dapat melakukan:

dari impor functools kurangi

sumOfNumbers = kurangi (lambda x, y: x + y, angka)

print (sumOfNumbers)

Yogendra Singh
sumber
0

Salah satu keunggulan peta, filter, dan reduksi adalah seberapa terbaca mereka ketika Anda "rantai" mereka bersama untuk melakukan sesuatu yang kompleks. Namun, sintaks bawaan tidak terbaca dan semuanya "mundur". Jadi, saya sarankan menggunakan PyFunctionalpaket ( https://pypi.org/project/PyFunctional/ ). Berikut perbandingan keduanya:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

Versi PyFunctional

Sintaks yang sangat terbaca. Anda bisa mengatakan:

"Saya memiliki urutan tujuan penerbangan. Dari mana saya ingin mendapatkan kunci dict jika kota berada dalam nilai dict. Akhirnya, saring daftar kosong yang saya buat dalam proses."

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

Versi Python default

Semuanya mundur. Anda perlu mengatakan:

"OK, jadi, ada daftar. Saya ingin menyaring daftar kosong dari itu. Mengapa? Karena saya pertama kali mendapatkan kunci dict jika kota itu dalam nilai dict. Oh, daftar yang saya lakukan ini adalah flight_destinations_dict. "

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
Daniel
sumber