Fungsi reduce() di Python berakar dari dunia pemrograman fungsional. Pemrograman fungsional (FP) adalah paradigma pemrograman di mana program menghasilkan hasil dengan menerapkan fungsi pada data yang tidak dapat diubah.
Pola umum dalam gaya ini adalah "fold," yang meruntuhkan sebuah urutan menjadi satu hasil. Misalnya, mem-fold daftar angka [2, 4, 5, 3] dengan operasi penjumlahan menghasilkan 14 melalui langkah berturut-turut: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce() menggeneralisasi gagasan ini. Ia menerapkan operasi biner pada sebuah iterable hingga hanya tersisa hasil akhirnya.
Dalam artikel ini, saya akan membahas elemen kunci dari reduce() di Python dan memberikan beberapa contoh praktis. Jika Anda perlu penyegaran dasar-dasar Python, saya sarankan meninjau sumber berikut:
Python menyertakan fungsi pemrograman fungsional lain di Python, seperti map() dan filter().
Konstruk fungsional seperti map() dan filter() sudah ada di Python 1.0. Guido van Rossum kurang menyukainya, menyoroti bahwa reduce() sulit dibaca, dan for loop hampir selalu lebih mudah dipahami. Di Python 3.0, mengikuti PEP 3100, pengembang menghapus reduce() sebagai built-in dan memindahkannya ke modul functools. Pemindahan ke functools pada dasarnya menurunkannya menjadi alat untuk kasus khusus.
Mengapa Menggunakan reduce()?
Sebagian besar waktu, menurut saya pilihan yang lebih baik adalah menggunakan built-in atau loop. Namun, reduce() tetap menjadi pilihan yang baik dalam beberapa kasus penggunaan.
Pipa fungsi. Merangkai serangkaian transformasi (mungkin dinamis) dengan cara yang rapi.
Fold aljabar. Gunakan untuk operasi yang memiliki nilai identitas alami, seperti union himpunan dengan himpunan kosong atau operasi bitmask dengan nol.
Fold kustom tanpa built-in. Definisikan penggabungan sendiri secara spesifik domain saat tidak ada built-in yang tersedia.
Akumulator terstruktur. Lacak beberapa potongan state secara bersamaan di dalam satu fungsi akumulator kustom.
Cara Kerja reduce() di Python
Mari kita telusuri mekanisme reduce().
Tanda tangan fungsi dasar dari reduce() adalah
functools.reduce(function, iterable,[initializer])Didukung oleh
Fungsi reduce() menerima dua argumen wajib dan satu argumen opsional.
function: fungsi biner yang menentukan cara menggabungkan dua elemen,
iterable: urutan atau iterable yang akan direduksi, seperti list atau tuple, initializer(opsional): nilai awal untuk memberi seed pada fungsi.
Contoh langkah demi langkah:
[1, 3, 2, 7] → [4, 2, 7].
[4, 2, 7] → [6, 7].
[6, 7] → [13].
Hasil akhirnya adalah 13.
Contoh sederhana reduce()
Contoh mainan berikut menunjukkan mekanisme cara menggunakan reduce().
from functools importreduce
numbers =[2,4,6]
product =reduce(lambda x, y: x * y, numbers)# ((2 x 4) x 6) = 8 x 6 = 48
min_value =reduce(lambda x, y: x if x < y else y, numbers)# 2
words =['dog','cat','tree','pony']
str_concat =reduce(lambda x, y: x + y, words)# "dogcattreepony"Didukung oleh
Initializer
Tanpa initializer, reduce() menggunakan elemen pertama dari iterable sebagai nilai awalnya. Jika iterable kosong, reduce() akan melempar TypeError. Untuk membuat kode tangguh, berikan initializer untuk mendefinisikan perilaku pada masukan kosong.
from functools importreduce
words =[]# Error: reduce() of empty iterable with no initial value
str_concat =reduce(lambda x, y: x + y, words)# Correct: use empty string initializer
str_concat =reduce(lambda x, y: x + y, words,"")Didukung oleh
Initializer juga memberi seed hasil dengan kontainer kosong. Misalnya, Anda dapat menggabungkan kata menjadi daftar karakter datar.
from functools importreduce
words =['reduce','is','fun']
chars_list =reduce(lambda acc, word: acc +list(word), words,[])print(chars_list)# ['r', 'e', 'd', 'u', 'c', 'e', 'i', 's', 'f', 'u', 'n']Didukung oleh
Untuk mengeksplorasi Python dan manipulasi data lebih jauh, berikut beberapa opsi yang saya rekomendasikan.
Sejauh ini, kita menggunakan fungsi lambda untuk mendefinisikan operator biner.
Anda juga dapat menggunakan operator dari modul operator. Modul operatorberisi versi fungsi dari operator umum dan pemanggilan metode. Misalnya, alih-alih x + y, gunakan operator.add(x, y). Ini memungkinkan Anda memasukkan operator siap pakai (dan efisien) ke reduce() tanpa perlu menulis lambda.
from functools importreduceimport operator as op
numbers =[2,4,6]
total =reduce(op.add, numbers,0)# instead of reduce(lambda x,y: x + y, numbers)Didukung oleh
Opsi ketiga adalah menulis fungsi kustom. Ini pilihan yang baik saat tidak ada operator yang sudah tersedia atau fungsinya terlalu rumit untuk lambda.
Sebagai contoh, misalkan Anda ingin menghapus duplikasi dari sebuah list, tetapi mempertahankan urutan kemunculan pertama. Anda dapat mendefinisikan reducer yang menambahkan item ke list hanya jika item tersebut belum muncul sebelumnya.
from functools importreduce
items =['the','wild','wild','world','is','the','wide','world','is','the','world']defdedup(acc, x):if x notin acc:# O(n) membership test
acc.append(x)return acc
unique =reduce(dedup, items,[])# ['the', 'wild', 'world', 'is', 'wide']Didukung oleh
Untuk ide lebih lanjut tentang menghapus duplikasi, pertimbangkan tutorial ini.
Python reduce() memanggil fungsi kita sekali untuk setiap elemen. Pada list dengan satu juta item, ini berarti satu juta pemanggilan fungsi, masing-masing membuat frame, menangani argumen, dan memperbarui reference count. Ini menambah overhead yang signifikan.
Sebaliknya, built-in seperti sum melakukan satu pemanggilan ke fungsi C dan menjalankan sejuta penjumlahan di dalam loop C. Perbedaan ini dapat membuat built-in jauh lebih cepat.
Lokalitas cache dan efisiensi CPU
Reduce juga terkendala lokalitas cache dan efisiensi CPU.
CPU modern dapat mengeksekusi beberapa miliar instruksi per detik, tetapi akses RAM jauh lebih lambat. Untuk mengompensasi, CPU modern memiliki cache (L1, L2, L3) yang menyimpan data untuk akses cepat.
Cache ini mengeksploitasi dua pola.
Lokalitas temporal: data yang baru digunakan kemungkinan akan digunakan lagi.
Lokalitas spasial: data yang dekat dengan alamat yang baru digunakan kemungkinan akan segera digunakan.
Sebaliknya, setiap langkah reduce() melibatkan penelusuran pointer untuk menemukan elemen berikutnya dan pemanggilan fungsi Python, yang merusak lokalitas dan membuat CPU tersendat. Built-in dan fungsi tervektorisasi menghindari masalah ini dengan berjalan pada loop C yang rapat.
Untuk penyegar tentang cara menulis kode Python yang idiomatis dan efisien, lihat:
Kode C yang dioptimalkan. Built-in menjalankan loop mereka dalam kode C yang dioptimalkan, bukan Python. Itu menghindari overhead yang ditimbulkan reduce(). Keunggulan kecepatan ini semakin terasa pada input besar.
Keterbacaan. Built-in memiliki nama deskriptif (sum, min) sehingga maksudnya jelas. Panggilan ke reduce() mengharuskan Anda membaca fungsi yang dilipat.
Loop:
Kinerja. Secara umum loop berjalan lebih lambat daripada built-in tetapi lebih cepat daripada reduce().
Keterbacaan. Seperti built-in, loop biasanya lebih mudah dibaca daripada reduce(). Panggilan ke reduce() memaksa Anda mem-parsing pernyataan fungsional, sedangkan loop lebih Pythonic.
itertools.accumulate()
Pustaka itertools di Python menyediakan kumpulan iterator berkinerja tinggi seperti count(), product(), dan combinations(). Salah satu fungsi itertools yang berguna adalah itertools.accumulate(). Seperti reduce(), ia melipat sebuah fungsi pada iterable. Namun, accumulate() menyimpan nilai perantara dari komputasi, bukan hanya hasil akhirnya.
Sebagai contoh,
import itertools, operator
from functools importreducelist(itertools.accumulate([1,2,3,4], initial=0))# [0, 1, 3, 6, 10]reduce(operator.add,[1,2,3,4],0)# 10Didukung oleh
Accumulate berguna saat Anda membutuhkan total berjalan atau nilai minimum/maksimum berjalan. Misalnya, Anda mungkin ingin mengetahui suhu maksimum dari bulan ke bulan.
Kesalahan Umum Saat Menggunakan reduce()
Saat Anda menggunakan reduce(), ingat jebakan berikut.
Lebih suka alternatif yang lebih sederhana seperti built-in atau loop. Simpan reduce() untuk saat Anda benar-benar membutuhkannya.
Tangani iterable kosong. Selalu gunakan initializer yang sesuai untuk menghindari error pada input kosong.
Perhatikan masalah memori. Jangan memaksakan reduce() ke situasi di mana pendekatan generator atau streaming akan lebih efisien.
Hindari lambda yang rumit. Gunakan fungsi dari modul operator bila memungkinkan. Lambda, khususnya dengan operasi non-asosiatif, dapat mengurangi kejelasan.
Utamakan kejelasan daripada kelihaian.
Praktik Terbaik dan Pedoman reduce() di Python
Seperti alat apa pun, ada praktik terbaik untuk reduce(). Jika Anda telah memutuskan reduce() adalah alat yang tepat, berikut beberapa pedoman penggunaannya.
Rancang reducer terlebih dahulu
Definisikan kontrak dalam satu kalimat. "Gabungkan kunci kamus dengan menjumlahkan hitungan per kunci."
Usahakan bersifat asosiatif jika memungkinkan. Ini memudahkan proses paralelisasi dan pengujian.
Identifikasi elemen identitas Anda dan gunakan sebagai initializer. Misalnya, untuk penjumlahan, identitasnya adalah 0. Untuk min, adalah math.inf, dan untuk set union, adalah set(). Ini membuat kode tangguh dan bebas dari TypeError.
Jaga tetap sederhana dan mudah dibaca
Satu operasi yang jelas. Tanpa efek samping.
Beri nama deskriptif pada reducer.
Dokumentasikan
Di docstring, catat elemen identitas dan perilaku input kosong, asumsi asosiativitas, kebijakan error.
Uji
Uji unit pada kasus tepi: iterable kosong, tipe campuran, nilai ekstrem.
Uji asosiativitas.
Pantau kinerja
Benchmark input kecil dan besar. Bandingkan benchmark dengan built-in dan loop.
Jika kecepatan penting, pertimbangkan prapemrosesan, batching, dan memindahkan perhitungan berat ke NumPy atau pandas.
Aplikasi Lanjutan dan Kasus Penggunaan Nyata untuk reduce() di Python
Melihat kekurangannya, mungkin tampak bahwa reduce() tidak memiliki nilai nyata. Sebaliknya, reduce() memiliki banyak kasus penggunaan yang bermanfaat.
Memproses struktur bersarang
Operasi bergaya basis data
Pipa pemrosesan data
Aplikasi MapReduce
Memproses struktur bersarang
Reduce menyediakan cara yang rapi untuk menelusuri struktur data bersarang, seperti objek JSON, dengan mem-fold urutan kunci menjadi lookup berturut-turut.
import json
from functools importreduceimport operator
data = json.loads('''
{
"user": {
"id": "ABC123",
"name": "Alice",
"email": "[email protected]",
"profile": {
"address": {
"city": "San Francisco",
"zip": "94103"
},
"age": 34,
"skills": ["Python", "Data Science", "Machine Learning"]
}
}
}
''')# Example lookups with reduce + operator.getitem
city =reduce(operator.getitem,["user","profile","address","city"], data)print(city)# "San Francisco"
user_id =reduce(operator.getitem,["user","id"], data)print(user_id)# "ABC123"
age =reduce(operator.getitem,["user","profile","age"], data)print(age)# 34Didukung oleh
Menggunakan reduce() masuk akal di sini. JSON-nya bersarang dalam: user → profile → address → city. Alih-alih merangkai lookup secara manual, wakili jalur sebagai list kunci. Lalu gunakan reduce(operator.getitem, path, data) untuk menelusurinya. Ini menjaga kode tetap generik, mudah dibaca, dan dapat digunakan ulang.
Pipa pemrosesan data
Reduce dapat menggerakkan pipa pemrosesan data dengan meneruskan data melalui rangkaian transformasi. Setiap fungsi menangani satu langkah, dan pipa dihasilkan dari menerapkannya secara berurutan. Berikut pipa sederhana yang mempraproses string teks sebelum dimasukkan ke model NLP.
from functools importreduceimport re
# Define preprocessing stepsdefstrip_punctuation(s):return re.sub(r"[^\w\s]","", s)defto_lower(s):return s.lower()defremove_stopwords(s):
stops ={"the","is","a","of"}return" ".join(word for word in s.split()if word notin stops)defstem_words(s):# trivial "stemmer": cut off 'ing'return" ".join(word[:-3]if word.endswith("ing")else word for word in s.split())
pipeline =[
strip_punctuation,
to_lower,
remove_stopwords,
stem_words,]# Input data
text ="The quick brown fox is Jumping over a log."# Apply pipeline with reduce
processed =reduce(lambda acc, f: f(acc), pipeline, text)print(processed)# quick brown fox jump over logDidukung oleh
Penanganan error dalam aplikasi kompleks
Mari kembali ke contoh JSON bersarang. Saat ini, pemanggilan langsung ke reduce(operator.getitem, …) akan melempar KeyError atau TypeError jika sebuah kunci hilang atau jika bertemu non-dict. Untuk membuat kode lebih aman, definisikan fungsi pembantu yang membungkus operator.getitem dalam blok try/except dan mengembalikan nilai default saat terjadi error.
Berikut salah satu kemungkinan untuk fungsi pembantu.
defdeep_get(data, keys, default=None):"""Traverse nested dicts/lists safely with reduce."""try:returnreduce(operator.getitem, keys, data)except(KeyError, IndexError, TypeError):return defaultDidukung oleh
Sekarang, ubah contoh lookup untuk menggunakan fungsi baru kita alih-alih reduce() yang tidak dibungkus
# Example lookups
city = deep_get(data,["user","profile","address","city"], default="Unknown City")print(city)# "San Francisco"
user_id = deep_get(data,["user","id"], default="N/A")print(user_id)# "ABC123"
age = deep_get(data,["user","profile","age"], default="N/A")print(age)# 34# Example with missing key
phone = deep_get(data,["user","profile","phone"], default="No phone")print(phone)# "No phone"Didukung oleh
Transformasi data multi-langkah dengan map() dan filter()
Anda dapat menggabungkan reduce() dengan alat fungsional lain, seperti map() dan filter(), untuk membangun transformasi data multi-langkah. Berikut pipa prapemrosesan NLP kita sebelumnya yang ditulis secara fungsional.
from functools importreduceimport re
# Define preprocessing stepsdefstrip_punctuation(s):return re.sub(r"[^\w\s]","", s)defto_lower(s):return" ".join(map(str.lower, s.split()))defremove_stopwords(s):
stops ={"the","is","a","of"}return" ".join(filter(lambda w: w notin stops, s.split()))defstem_words(s):# trivial "stemmer": cut off 'ing'return" ".join(map(lambda w: w[:-3]if w.endswith("ing")else w, s.split()))# Pipeline of transformations
pipeline =[
strip_punctuation,
to_lower,
remove_stopwords,
stem_words,]# Input data
text ="The quick brown fox is Jumping over a log."# Apply pipeline with reduce
processed =reduce(lambda acc, f: f(acc), pipeline, text)print(processed)# quick brown fox jump over logDidukung oleh
Transformasinya adalah:
Hapus tanda baca. "The quick brown fox is Jumping over a log." → "The quick brown fox is Jumping over a log"
Huruf kecil
"The quick brown fox is Jumping over a log" → "the quick brown fox is jumping over a log"
Hapus stop words. "the quick brown fox is jumping over a log" → "quick brown fox jumping over log"
Stem kata
"quick brown fox jumping log" → "quick brown fox jump log"
Untuk mengeksplorasi lebih lanjut gagasan pemrograman fungsional dan vektorisasi, kami merekomendasikan artikel DataCamp berikut.
Untuk menggunakan reduce() dengan baik, akan membantu jika memahami bagaimana ia cocok dalam ekosistem Python modern. Mari telusuri bagaimana posisinya bersama NumPy dan pandas, bagaimana ia menopang sistem paralel dan terdistribusi, serta bagaimana interaksinya dengan tooling modern seperti analis statis.
numpy dan pandas
NumPy dan pandas bekerja dari kode C yang dioptimalkan, jadi jangan duplikasi fungsionalitasnya dengan reduce(). Namun, reduce() adalah pilihan yang baik untuk pipa dengan langkah-langkah dinamis. Misalnya, Anda mungkin menggabungkan banyak transformasi NumPy pada sebuah array.
from functools importreduceimport numpy as np
defstandardize(x):return(x - x.mean())/(x.std()+1e-9)defclip(x):return np.clip(x,0,1)deflog(x):return np.log1p(x)
x = np.array([1,500,40.5,100,250.45])
funcs =[standardize, clip, log]
y =reduce(lambda a, f: f(a), funcs, x)# x is ndarrayDidukung oleh
Kerangka komputasi paralel dan terdistribusi
Reduce adalah inti dari kerangka komputasi paralel dan terdistribusi. Sistem-sistem ini bekerja dengan membagi dataset menjadi partisi, memproses partisi tersebut secara paralel, lalu menggabungkan hasil parsial menjadi satu jawaban. Langkah "combine" adalah reduksi.
Agar reduksi berjalan dengan benar di seluruh klaster, pastikan kondisi berikut terpenuhi.
Asosiativitas. Operasi harus memberikan hasil yang sama terlepas dari pengelompokan. Ini memungkinkan hasil parsial digabungkan dalam urutan apa pun di seluruh jaringan.
Elemen identitas. Operasi harus memiliki initializer yang tidak memengaruhi hasil akhir. Ini memastikan kebenaran saat partisi kosong atau berukuran tidak merata.
Jika properti ini tidak terpenuhi, reduksi menjadi lambat (karena tidak dapat diparalelkan dengan aman) atau tidak benar (karena hasil bergantung pada urutan evaluasi).
Proses ini membutuhkan operasi yang asosiatif dan identitas yang jelas. Jika tidak, Anda mendapatkan kode yang lambat atau hasil yang salah.
Alat analisis statis
Analyzer statis (seperti mypy, Pyright, ruff, atau bandit) adalah alat yang memeriksa kode tanpa mengeksekusinya. Alat ini menangkap bug, menegakkan aturan gaya, dan memeriksa kebenaran tipe.
Analyzer statis kesulitan dengan reduce(). Dalam proses folding, tipe akumulator bisa berbeda dari tipe elemen, dan inferensi tipe menjadi berantakan.
Pertimbangkan kode berikut.
from functools importreducedefadd_chars(acc, word):
acc.extend(word)# extend adds each character of the stringreturn acc
chars =reduce(add_chars,["hi","ok"],[])print(chars)# ['h', 'i', 'o', 'k']Didukung oleh
Meskipun kode ini berjalan baik, analyzer statis mungkin mengeluhkan beberapa hal.
Tipe elemen dari initializer [] ambigu.
Analyzer mungkin mengasumsikan tipe akumulator dan elemen harus cocok.
Untuk membuat kode lebih jelas bagi manusia dan analyzer, tambahkan type hint.
from functools importreducefrom typing import List
defadd_chars(acc: List[str], word:str)-> List[str]:
acc.extend(word)# extend adds each character of the stringreturn acc
chars: List[str]=reduce(add_chars,["hi","ok"],[])print(chars)# ['h', 'i', 'o', 'k']Didukung oleh
Sekarang reducer secara eksplisit menunjukkan
Akumulator adalah List[str].
Setiap elemen adalah str.
Tipe nilai kembalian adalah list[str].
Dengan hint ini, analyzer statis dapat memeriksa kode secara ketat.
Kesimpulan
Reduce berasal dari pemrograman fungsional, di mana melipat koleksi menjadi satu hasil adalah gagasan inti. Meskipun tidak lagi menjadi alat kelas satu di Python, ia tetap memiliki tempatnya. Saat Anda membutuhkan pipa yang fleksibel, fold kustom, atau operasi yang tidak secara mulus dipetakan ke fungsi yang ada, reduce() adalah alat yang kuat. Jika digunakan dengan hati-hati, ia terintegrasi dengan baik dengan ekosistem Python yang lebih luas dan tetap menjadi alat yang praktis dalam situasi yang tepat.
Beberapa tautan Python terkait yang mungkin berguna.
Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Kembangkan keterampilan data Anda, pelajari cara mengolah dan memvisualisasikan data, serta terapkan analisis canggih untuk mengambil keputusan berbasis data.
Fungsi
reduce()di Python berakar dari dunia pemrograman fungsional. Pemrograman fungsional (FP) adalah paradigma pemrograman di mana program menghasilkan hasil dengan menerapkan fungsi pada data yang tidak dapat diubah.Pola umum dalam gaya ini adalah "fold," yang meruntuhkan sebuah urutan menjadi satu hasil. Misalnya, mem-fold daftar angka [2, 4, 5, 3] dengan operasi penjumlahan menghasilkan 14 melalui langkah berturut-turut: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce()menggeneralisasi gagasan ini. Ia menerapkan operasi biner pada sebuah iterable hingga hanya tersisa hasil akhirnya.Dalam artikel ini, saya akan membahas elemen kunci dari
reduce()di Python dan memberikan beberapa contoh praktis. Jika Anda perlu penyegaran dasar-dasar Python, saya sarankan meninjau sumber berikut:Sejarah
Python menyertakan fungsi pemrograman fungsional lain di Python, seperti
map()danfilter().Konstruk fungsional seperti
map()danfilter()sudah ada di Python 1.0. Guido van Rossum kurang menyukainya, menyoroti bahwareduce()sulit dibaca, danforloop hampir selalu lebih mudah dipahami. Di Python 3.0, mengikuti PEP 3100, pengembang menghapusreduce()sebagai built-in dan memindahkannya ke modulfunctools. Pemindahan kefunctoolspada dasarnya menurunkannya menjadi alat untuk kasus khusus.Mengapa Menggunakan reduce()?
Sebagian besar waktu, menurut saya pilihan yang lebih baik adalah menggunakan built-in atau loop. Namun,
reduce()tetap menjadi pilihan yang baik dalam beberapa kasus penggunaan.Cara Kerja reduce() di Python
Mari kita telusuri mekanisme
reduce().Tanda tangan fungsi dasar dari
reduce()adalahFungsi
reduce()menerima dua argumen wajib dan satu argumen opsional.function: fungsi biner yang menentukan cara menggabungkan dua elemen,iterable: urutan atau iterable yang akan direduksi, seperti list atau tuple,initializer(opsional): nilai awal untuk memberi seed pada fungsi.Contoh langkah demi langkah:
Hasil akhirnya adalah 13.
Contoh sederhana reduce()
Contoh mainan berikut menunjukkan mekanisme cara menggunakan
reduce().Initializer
Tanpa initializer,
reduce()menggunakan elemen pertama dari iterable sebagai nilai awalnya. Jika iterable kosong,reduce()akan melemparTypeError. Untuk membuat kode tangguh, berikan initializer untuk mendefinisikan perilaku pada masukan kosong.Initializer juga memberi seed hasil dengan kontainer kosong. Misalnya, Anda dapat menggabungkan kata menjadi daftar karakter datar.
Untuk mengeksplorasi Python dan manipulasi data lebih jauh, berikut beberapa opsi yang saya rekomendasikan.
Mendefinisikan reducer
Sejauh ini, kita menggunakan fungsi lambda untuk mendefinisikan operator biner.
Anda juga dapat menggunakan operator dari modul
operator. Moduloperatorberisi versi fungsi dari operator umum dan pemanggilan metode. Misalnya, alih-alih x + y, gunakanoperator.add(x, y). Ini memungkinkan Anda memasukkan operator siap pakai (dan efisien) kereduce() tanpa perlu menulis lambda.Opsi ketiga adalah menulis fungsi kustom. Ini pilihan yang baik saat tidak ada operator yang sudah tersedia atau fungsinya terlalu rumit untuk lambda.
Sebagai contoh, misalkan Anda ingin menghapus duplikasi dari sebuah list, tetapi mempertahankan urutan kemunculan pertama. Anda dapat mendefinisikan reducer yang menambahkan item ke list hanya jika item tersebut belum muncul sebelumnya.
Untuk ide lebih lanjut tentang menghapus duplikasi, pertimbangkan tutorial ini.
Kinerja reduce() di Python
reduce()memiliki isu kinerja.Overhead pemanggilan fungsi
Python
reduce()memanggil fungsi kita sekali untuk setiap elemen. Pada list dengan satu juta item, ini berarti satu juta pemanggilan fungsi, masing-masing membuat frame, menangani argumen, dan memperbarui reference count. Ini menambah overhead yang signifikan.Sebaliknya, built-in seperti
summelakukan satu pemanggilan ke fungsi C dan menjalankan sejuta penjumlahan di dalam loop C. Perbedaan ini dapat membuat built-in jauh lebih cepat.Lokalitas cache dan efisiensi CPU
Reduce juga terkendala lokalitas cache dan efisiensi CPU.
CPU modern dapat mengeksekusi beberapa miliar instruksi per detik, tetapi akses RAM jauh lebih lambat. Untuk mengompensasi, CPU modern memiliki cache (L1, L2, L3) yang menyimpan data untuk akses cepat.
Cache ini mengeksploitasi dua pola.
Sebaliknya, setiap langkah
reduce()melibatkan penelusuran pointer untuk menemukan elemen berikutnya dan pemanggilan fungsi Python, yang merusak lokalitas dan membuat CPU tersendat. Built-in dan fungsi tervektorisasi menghindari masalah ini dengan berjalan pada loop C yang rapat.Untuk penyegar tentang cara menulis kode Python yang idiomatis dan efisien, lihat:
Alternatif untuk reduce() di Python
Built-in:
reduce(). Keunggulan kecepatan ini semakin terasa pada input besar.sum,min) sehingga maksudnya jelas. Panggilan kereduce()mengharuskan Anda membaca fungsi yang dilipat.Loop:
reduce().reduce(). Panggilan kereduce()memaksa Anda mem-parsing pernyataan fungsional, sedangkan loop lebih Pythonic.itertools.accumulate()Pustaka
itertoolsdi Python menyediakan kumpulan iterator berkinerja tinggi seperticount(),product(), dancombinations(). Salah satu fungsiitertoolsyang berguna adalahitertools.accumulate(). Sepertireduce(), ia melipat sebuah fungsi pada iterable. Namun,accumulate() menyimpan nilai perantara dari komputasi, bukan hanya hasil akhirnya.Sebagai contoh,
Accumulate berguna saat Anda membutuhkan total berjalan atau nilai minimum/maksimum berjalan. Misalnya, Anda mungkin ingin mengetahui suhu maksimum dari bulan ke bulan.
Kesalahan Umum Saat Menggunakan reduce()
Saat Anda menggunakan
reduce(), ingat jebakan berikut.reduce()untuk saat Anda benar-benar membutuhkannya.reduce()ke situasi di mana pendekatan generator atau streaming akan lebih efisien.operatorbila memungkinkan. Lambda, khususnya dengan operasi non-asosiatif, dapat mengurangi kejelasan.Praktik Terbaik dan Pedoman reduce() di Python
Seperti alat apa pun, ada praktik terbaik untuk
reduce(). Jika Anda telah memutuskanreduce()adalah alat yang tepat, berikut beberapa pedoman penggunaannya.Rancang reducer terlebih dahulu
min, adalahmath.inf, dan untukset union, adalahset(). Ini membuat kode tangguh dan bebas dariTypeError.Jaga tetap sederhana dan mudah dibaca
Dokumentasikan
Uji
Pantau kinerja
Aplikasi Lanjutan dan Kasus Penggunaan Nyata untuk reduce() di Python
Melihat kekurangannya, mungkin tampak bahwa
reduce()tidak memiliki nilai nyata. Sebaliknya,reduce()memiliki banyak kasus penggunaan yang bermanfaat.Memproses struktur bersarang
Reduce menyediakan cara yang rapi untuk menelusuri struktur data bersarang, seperti objek JSON, dengan mem-fold urutan kunci menjadi lookup berturut-turut.
Menggunakan
reduce()masuk akal di sini. JSON-nya bersarang dalam:user → profile → address → city. Alih-alih merangkai lookup secara manual, wakili jalur sebagai list kunci. Lalu gunakanreduce(operator.getitem, path, data)untuk menelusurinya. Ini menjaga kode tetap generik, mudah dibaca, dan dapat digunakan ulang.Pipa pemrosesan data
Reduce dapat menggerakkan pipa pemrosesan data dengan meneruskan data melalui rangkaian transformasi. Setiap fungsi menangani satu langkah, dan pipa dihasilkan dari menerapkannya secara berurutan. Berikut pipa sederhana yang mempraproses string teks sebelum dimasukkan ke model NLP.
Penanganan error dalam aplikasi kompleks
Mari kembali ke contoh JSON bersarang. Saat ini, pemanggilan langsung ke
reduce(operator.getitem, …)akan melemparKeyErroratauTypeErrorjika sebuah kunci hilang atau jika bertemu non-dict. Untuk membuat kode lebih aman, definisikan fungsi pembantu yang membungkusoperator.getitemdalam blok try/except dan mengembalikan nilai default saat terjadi error.Berikut salah satu kemungkinan untuk fungsi pembantu.
Sekarang, ubah contoh lookup untuk menggunakan fungsi baru kita alih-alih
reduce()yang tidak dibungkusTransformasi data multi-langkah dengan map() dan filter()
Anda dapat menggabungkan
reduce()dengan alat fungsional lain, sepertimap()danfilter(), untuk membangun transformasi data multi-langkah. Berikut pipa prapemrosesan NLP kita sebelumnya yang ditulis secara fungsional.Transformasinya adalah:
"The quick brown fox is Jumping over a log" → "the quick brown fox is jumping over a log"
"quick brown fox jumping log" → "quick brown fox jump log"
Untuk mengeksplorasi lebih lanjut gagasan pemrograman fungsional dan vektorisasi, kami merekomendasikan artikel DataCamp berikut.
Integrasi dengan Ekosistem Python Modern
Untuk menggunakan
reduce()dengan baik, akan membantu jika memahami bagaimana ia cocok dalam ekosistem Python modern. Mari telusuri bagaimana posisinya bersama NumPy dan pandas, bagaimana ia menopang sistem paralel dan terdistribusi, serta bagaimana interaksinya dengan tooling modern seperti analis statis.numpy dan pandas
NumPy dan pandas bekerja dari kode C yang dioptimalkan, jadi jangan duplikasi fungsionalitasnya dengan
reduce(). Namun,reduce()adalah pilihan yang baik untuk pipa dengan langkah-langkah dinamis. Misalnya, Anda mungkin menggabungkan banyak transformasi NumPy pada sebuah array.Kerangka komputasi paralel dan terdistribusi
Reduce adalah inti dari kerangka komputasi paralel dan terdistribusi. Sistem-sistem ini bekerja dengan membagi dataset menjadi partisi, memproses partisi tersebut secara paralel, lalu menggabungkan hasil parsial menjadi satu jawaban. Langkah "combine" adalah reduksi.
Agar reduksi berjalan dengan benar di seluruh klaster, pastikan kondisi berikut terpenuhi.
Jika properti ini tidak terpenuhi, reduksi menjadi lambat (karena tidak dapat diparalelkan dengan aman) atau tidak benar (karena hasil bergantung pada urutan evaluasi).
Proses ini membutuhkan operasi yang asosiatif dan identitas yang jelas. Jika tidak, Anda mendapatkan kode yang lambat atau hasil yang salah.
Alat analisis statis
Analyzer statis (seperti mypy, Pyright, ruff, atau bandit) adalah alat yang memeriksa kode tanpa mengeksekusinya. Alat ini menangkap bug, menegakkan aturan gaya, dan memeriksa kebenaran tipe.
Analyzer statis kesulitan dengan
reduce(). Dalam proses folding, tipe akumulator bisa berbeda dari tipe elemen, dan inferensi tipe menjadi berantakan.Pertimbangkan kode berikut.
Meskipun kode ini berjalan baik, analyzer statis mungkin mengeluhkan beberapa hal.
Untuk membuat kode lebih jelas bagi manusia dan analyzer, tambahkan type hint.
Sekarang reducer secara eksplisit menunjukkan
List[str].str.list[str].Dengan hint ini, analyzer statis dapat memeriksa kode secara ketat.
Kesimpulan
Reduce berasal dari pemrograman fungsional, di mana melipat koleksi menjadi satu hasil adalah gagasan inti. Meskipun tidak lagi menjadi alat kelas satu di Python, ia tetap memiliki tempatnya. Saat Anda membutuhkan pipa yang fleksibel, fold kustom, atau operasi yang tidak secara mulus dipetakan ke fungsi yang ada,
reduce()adalah alat yang kuat. Jika digunakan dengan hati-hati, ia terintegrasi dengan baik dengan ekosistem Python yang lebih luas dan tetap menjadi alat yang praktis dalam situasi yang tepat.Beberapa tautan Python terkait yang mungkin berguna.