Python का reduce() फ़ंक्शन फंक्शनल प्रोग्रामिंग की दुनिया से आता है। फंक्शनल प्रोग्रामिंग (FP) एक प्रोग्रामिंग पैरेडाइम है जिसमें प्रोग्राम, अपरिवर्तनीय डेटा पर फ़ंक्शनों को लागू करके परिणाम बनाते हैं।
इस शैली में एक आम पैटर्न "फोल्ड" है, जो किसी अनुक्रम को एकल परिणाम में समेट देता है। उदाहरण के लिए, संख्याओं की सूची [2, 4, 5, 3] को जोड़ के तहत क्रमिक चरणों से 14 में फोल्ड करना: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce() इसी विचार को सामान्यीकृत करता है। यह एक बाइनरी ऑपरेशन को किसी iterable पर तब तक लागू करता है जब तक केवल परिणाम न बच जाए।
इस लेख में, मैं Python के reduce() के मुख्य तत्वों की पड़ताल करूँगा और कुछ व्यावहारिक उदाहरण दूँगा। यदि आपको Python की बुनियाद का पुनर्स्मरण चाहिए, तो मैं ये संसाधन देखने की सलाह देता हूँ:
Python में फंक्शनल प्रोग्रामिंग के अन्य फ़ंक्शन भी हैं, जैसे map() और filter().
फंक्शनल संरचनाएँ जैसे map() और filter() Python 1.0 में थीं। Guido van Rossum को वे पसंद नहीं थीं; उन्होंने बताया कि reduce() पढ़ना मुश्किल है, और लगभग हमेशा एक for लूप अधिक पठनीय होता है। Python 3.0 में, PEP 3100 के अनुरूप, डेवलपर्स ने reduce() को बिल्ट-इन से हटा कर functools मॉड्यूल में स्थानांतरित कर दिया। functools में यह बदलाव मूलतः इसे एक विशिष्ट (niche) टूल के दर्जे पर ले आया।
reduce() का उपयोग क्यों करें?
अधिकांश समय, बिल्ट-इन या लूप का उपयोग करना बेहतर विकल्प होता है। फिर भी, कुछ उपयोग मामलों में reduce() एक अच्छा चुनाव है।
फ़ंक्शन पाइपलाइन। रूपांतरणों की एक (संभवतः डायनेमिक) शृंखला को साफ़-सुथरे तरीके से जोड़ें।
बीजगणितीय फोल्ड। उन ऑपरेशनों के लिए उपयोग करें जिनके प्राकृतिक पहचान मान (identity values) होते हैं, जैसे खाली सेट के साथ सेट युनियन या शून्य के साथ बिटमास्क ऑपरेशन।
कोई बिल्ट-इन न होने पर कस्टम फोल्ड। जब कोई बिल्ट-इन मौजूद न हो, तो डोमेन-विशिष्ट तरीके से अपना मर्ज परिभाषित करें।
स्ट्रक्चर्ड एक्यूमुलेटर्स। एक कस्टम एक्यूमुलेटर फ़ंक्शन के भीतर एक साथ कई स्टेट्स को ट्रैक करें।
Python reduce() कैसे काम करता है
आइए reduce() की कार्यप्रणाली को समझें।
reduce() का मूल फ़ंक्शन सिग्नेचर है
functools.reduce(function, iterable,[initializer])संचालित द्वारा
यह reduce() दो अनिवार्य और एक वैकल्पिक तर्क लेता है।
function: एक बाइनरी फ़ंक्शन जो दो तत्वों को कैसे संयोजित करना है, यह बताता है,
iterable: वह अनुक्रम या iterable जिसे रिड्यूस करना है, जैसे सूची या ट्यूपल, initializer (वैकल्पिक): फ़ंक्शन को सीड करने के लिए प्रारंभिक मान।
चरण-दर-चरण उदाहरण:
[1, 3, 2, 7] → [4, 2, 7].
[4, 2, 7] → [6, 7].
[6, 7] → [13].
अंतिम परिणाम 13 है।
सरल reduce() उदाहरण
निम्नलिखित छोटे उदाहरण दिखाते हैं कि 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"संचालित द्वारा
इनिशियलाइज़र
इनिशियलाइज़र के बिना, reduce() iterable के पहले तत्व को अपने प्रारंभिक मान के रूप में लेता है। यदि iterable खाली है, तो reduce() एक TypeError फेंकता है। कोड को मज़बूत बनाने के लिए, खाली इनपुट पर व्यवहार परिभाषित करने हेतु एक इनिशियलाइज़र दें।
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,"")संचालित द्वारा
इनिशियलाइज़र किसी खाली कंटेनर से भी परिणाम को सीड करते हैं। उदाहरण के लिए, आप शब्दों को अक्षरों की फ्लैट सूची में जोड़ सकते हैं।
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']संचालित द्वारा
Python और डेटा मैनिपुलेशन को और गहराई से जानने के लिए, ये विकल्प उपयोगी हैं।
अब तक, हमने बाइनरी ऑपरेटर को परिभाषित करने के लिए लैम्ब्डा फ़ंक्शन का उपयोग किया है।
आप operator मॉड्यूल से ऑपरेटर भी उपयोग कर सकते हैं। operator मॉड्यूल में सामान्य ऑपरेटरों और मेथड कॉल्स के फ़ंक्शन रूप हैं। उदाहरण के लिए, x + y के बजाय operator.add(x, y)। इससे आप बिना लैम्ब्डा लिखे, पहले से परिभाषित (और कुशल) ऑपरेटरों को reduce() में पास कर सकते हैं।
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)संचालित द्वारा
तीसरा विकल्प एक कस्टम फ़ंक्शन लिखना है। यह तब अच्छा होता है जब कोई पूर्वनिर्धारित ऑपरेटर न हो या फ़ंक्शन लैम्ब्डा के लिए बहुत जटिल हो।
उदाहरण के लिए, मान लें कि आप सूची से डुप्लिकेट हटाना चाहते हैं, लेकिन पहली बार दिखने के क्रम को बनाए रखना चाहते हैं। आप ऐसा रिड्यूसर परिभाषित कर सकते हैं जो किसी आइटम को तभी सूची में जोड़ता है जब वह पहले न आया हो।
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']संचालित द्वारा
डुप्लिकेट हटाने के और विचारों के लिए, यह ट्यूटोरियल देखें।
Python में reduce() हमारा फ़ंक्शन iterable के हर तत्व के लिए एक बार कॉल करता है। यदि सूची में दस लाख आइटम हैं, तो इसका अर्थ है दस लाख फ़ंक्शन कॉल—हर कॉल एक फ़्रेम बनाती है, आर्ग्युमेंट सँभालती है, और रेफरेंस काउंट अपडेट करती है। यह काफ़ी ओवरहेड जोड़ता है।
इसके विपरीत, sum जैसा बिल्ट-इन एक ही बार C फ़ंक्शन में कॉल करता है और लाख जोड़ C लूप के भीतर करता है। यह फ़र्क बिल्ट-इन को कई गुना तेज़ बना सकता है।
कैश लोकैलिटी और CPU दक्षता से जुड़ी समस्याएँ
Reduce को कैश लोकैलिटी और CPU दक्षता से भी नुकसान होता है।
आधुनिक CPU प्रति सेकंड कई अरब निर्देश चला सकता है, लेकिन RAM एक्सेस उससे कई गुना धीमा है। इसकी भरपाई के लिए, आधुनिक CPU में कैश (L1, L2, L3) होते हैं जो तेज़ एक्सेस के लिए डेटा स्टोर करते हैं।
ये कैश दो पैटर्न का लाभ उठाते हैं।
टेम्पोरल लोकैलिटी — हाल में उपयोग किया गया डेटा फिर उपयोग होने की संभावना है।
स्पैशियल लोकैलिटी — हाल में उपयोग किए गए पते के पास का डेटा जल्द उपयोग होने की संभावना है।
इसके विपरीत, reduce() का हर चरण अगले तत्व को ढूँढने के लिए पॉइंटर चेज़िंग और Python फ़ंक्शन कॉल्स में जाता है, जो लोकैलिटी तोड़ते हैं और CPU को स्टॉल करते हैं। बिल्ट-इन्स और वेक्टराइज़्ड फ़ंक्शंस इस समस्या से बचते हैं क्योंकि वे कसे हुए C लूप्स में चलते हैं।
आदर्श और कुशल Python कोड लिखने के लिए पुनर्स्मरण हेतु ये देखें:
ऑप्टिमाइज़्ड C कोड। बिल्ट-इन्स अपने लूप्स Python में नहीं बल्कि ऑप्टिमाइज़्ड C कोड में चलाते हैं। इससे reduce() का ओवरहेड नहीं लगता। बड़े इनपुट पर यह गति लाभ और बढ़ जाता है।
पठनीयता। बिल्ट-इन्स के नाम वर्णनात्मक होते हैं (sum, min) इसलिए उनका आशय स्पष्ट होता है। reduce() कॉल में आपको फोल्ड की जा रही फ़ंक्शन को समझना पड़ता है।
लूप्स:
प्रदर्शन। लूप्स आम तौर पर बिल्ट-इन्स से धीमे लेकिन reduce() से तेज़ चलते हैं।
पठनीयता। बिल्ट-इन्स की तरह, लूप्स आम तौर पर reduce() से अधिक पठनीय होते हैं। reduce() आपको एक फंक्शनल स्टेटमेंट पार्स करने पर मजबूर करता है, जबकि लूप अधिक पाइथॉनिक है।
itertools.accumulate()
Python की itertools लाइब्रेरी में count(), product() और combinations() जैसे परफ़ॉर्मेंट इटरेटर्स का संग्रह है। एक उपयोगी itertools फ़ंक्शन है itertools.accumulate()। यह reduce() की तरह किसी iterable पर फ़ंक्शन को फोल्ड करता है। हालाँकि, accumulate() केवल अंतिम परिणाम नहीं, बल्कि मध्यवर्ती मान भी संग्रहीत करता है।
उदाहरण के लिए,
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)# 10संचालित द्वारा
Accumulate तब उपयोगी है जब आपको रनिंग टोटल्स या न्यूनतम/अधिकतम चाहिए। उदाहरण के लिए, आप महीने-दर-महीने अधिकतम तापमान जानना चाह सकते हैं।
reduce() का उपयोग करते समय सामान्य गलतियाँ
जब आप reduce() का उपयोग करें, तो निम्नलिखित बातों का ध्यान रखें।
जहाँ संभव हो, बिल्ट-इन्स या लूप्स जैसे सरल विकल्पों को प्राथमिकता दें। reduce() को तभी बचाएँ जब वास्तव में उसकी ज़रूरत हो।
खाली iterables को सँभालें। खाली इनपुट पर त्रुटि से बचने के लिए हमेशा उपयुक्त इनिशियलाइज़र का उपयोग करें।
मेमोरी समस्याओं पर नज़र रखें। उन स्थितियों में reduce() को ज़बरदस्ती न फिट करें जहाँ जेनरेटर या स्ट्रीमिंग तरीका अधिक कुशल होगा।
जटिल लैम्ब्डा से बचें। जहाँ संभव हो operator मॉड्यूल से फ़ंक्शंस उपयोग करें। खासकर गैर-सहसंयोजी (non-associative) ऑपरेशंस के साथ लैम्ब्डा स्पष्टता को नुकसान पहुँचा सकते हैं।
चतुराई से अधिक स्पष्टता को प्राथमिकता दें।
Python reduce() सर्वोत्तम प्रथाएँ और दिशानिर्देश
किसी भी टूल की तरह, reduce() के साथ भी सर्वोत्तम प्रथाएँ हैं। यदि आपने तय कर लिया है कि reduce() सही टूल है, तो इसके उपयोग के लिए कुछ दिशानिर्देश यहाँ हैं।
पहले रिड्यूसर डिज़ाइन करें
एक वाक्य में अनुबंध परिभाषित करें। "डिक्शनरी कीज़ को प्रति-की काउंट जोड़कर संयोजित करें।"
संभव हो तो इसे सहसंयोजी (associative) रखें। इससे पैरललाइज़ और परीक्षण करना आसान होता है।
अपना पहचान तत्व (identity element) पहचानें और उसे इनिशियलाइज़र के रूप में उपयोग करें। उदाहरण के लिए, sum के लिए पहचान 0 है। min के लिए math.inf, और set union के लिए set()। इससे कोड मज़बूत रहता है और TypeErrors से मुक्त रहता है।
इसे सरल और पठनीय रखें
एक स्पष्ट ऑपरेशन। कोई साइड इफेक्ट नहीं।
रिड्यूसर को वर्णनात्मक नाम दें।
दस्तावेज़ करें
डॉकस्ट्रिंग में पहचान तत्व और खाली इनपुट पर व्यवहार, सहसंयोजकता मान्यता, त्रुटि नीति दर्ज करें।
परीक्षण करें
एज केस पर यूनिट टेस्ट: खाली iterable, मिश्रित प्रकार, चरम मान।
सहसंयोजकता का परीक्षण करें।
प्रदर्शन मॉनिटर करें
छोटे और बड़े इनपुट पर बेंचमार्क करें। बिल्ट-इन्स और लूप्स से तुलना करें।
यदि गति महत्वपूर्ण है, तो प्रीप्रोसेसिंग, बैचिंग पर विचार करें, और भारी गणना NumPy या pandas में ले जाएँ।
Python reduce() के उन्नत उपयोग और वास्तविक-life केस
खामियों के बावजूद, ऐसा लग सकता है कि reduce() का कोई वास्तविक मूल्य नहीं है। इसके विपरीत, reduce() के कई उपयोगी उपयोग मामले हैं।
नेस्टेड संरचनाओं का प्रोसेसिंग
डेटाबेस-शैली के ऑपरेशन
डेटा प्रोसेसिंग पाइपलाइन्स
MapReduce एप्लिकेशन
नेस्टेड संरचनाओं का प्रोसेसिंग
Reduce, JSON ऑब्जेक्ट्स जैसी नेस्टेड डेटा संरचनाओं को ट्रैवर्स करने का एक साफ़ तरीका देता है, जिसमें कुंजियों की शृंखला को क्रमिक लुकअप्स में फोल्ड किया जाता है।
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)# 34संचालित द्वारा
यहाँ reduce() का उपयोग समझ में आता है। JSON गहराई से नेस्टेड है: user → profile → address → city। मैन्युअली लुकअप्स चेन करने के बजाय, पथ को कुंजियों की सूची के रूप में दर्शाएँ। फिर reduce(operator.getitem, path, data) का उपयोग कर उसे ट्रैवर्स करें। इससे कोड जेनेरिक, पठनीय और पुन: प्रयोज्य बनता है।
डेटा प्रोसेसिंग पाइपलाइन्स
Reduce, डेटा को रूपांतरणों की एक शृंखला से गुज़ार कर पाइपलाइन्स चला सकता है। हर फ़ंक्शन एक चरण सँभालता है, और पाइपलाइन उन्हीं को क्रम में लागू करने से बनती है। यहाँ एक छोटा उदाहरण है जो किसी 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 logसंचालित द्वारा
जटिल एप्लिकेशनों में त्रुटि हैंडलिंग
आइए नेस्टेड JSON वाले उदाहरण पर लौटें। फिलहाल, सीधे reduce(operator.getitem, …) कॉल करने पर यदि कोई कुंजी गायब हो या non-dict मिल जाए तो KeyError या TypeError आता है। कोड को सुरक्षित बनाने के लिए, एक सहायक फ़ंक्शन परिभाषित करें जो operator.getitem को try/except में रैप करे और त्रुटि पर डिफ़ॉल्ट मान लौटाए।
सहायक फ़ंक्शन का एक विकल्प यहाँ है।
defdeep_get(data, keys, default=None):"""Traverse nested dicts/lists safely with reduce."""try:returnreduce(operator.getitem, keys, data)except(KeyError, IndexError, TypeError):return defaultसंचालित द्वारा
अब, अनरैप्ड reduce() के बजाय हमारे नए फ़ंक्शन का उपयोग करके उदाहरण लुकअप्स बदलें
# 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"संचालित द्वारा
map() और filter() के साथ बहु-चरणीय डेटा रूपांतरण
आप reduce() को अन्य फंक्शनल टूल्स, जैसे map() और filter() के साथ जोड़कर बहु-चरणीय डेटा रूपांतरण बना सकते हैं। यहाँ हमारा पहले वाला NLP प्रीप्रोसेसिंग पाइपलाइन फंक्शनल रूप में है।
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 logसंचालित द्वारा
रूपांतरण ये हैं:
विराम चिह्न हटाना। "The quick brown fox is Jumping over a log." → "The quick brown fox is Jumping over a log"
लोअर केस
"The quick brown fox is Jumping over a log" → "the quick brown fox is jumping over a log"
स्टॉप वर्ड्स हटाएँ। "the quick brown fox is jumping over a log" → "quick brown fox jumping over log"
स्टेमिंग
"quick brown fox jumping log" → "quick brown fox jump log"
फंक्शनल प्रोग्रामिंग और वेक्टराइज़ेशन विचारों को आगे जानने के लिए, हम ये DataCamp लेख सुझाते हैं।
reduce() का अच्छा उपयोग करने के लिए यह समझना उपयोगी है कि यह आधुनिक Python इकोसिस्टम में कैसे फिट होता है। आइए देखें कि यह NumPy और pandas के साथ कैसे बैठता है, यह पैरलल और डिस्ट्रीब्यूटेड सिस्टम्स की रीढ़ कैसे है, और यह mypy जैसे स्टैटिक एनालाइज़र्स सहित आधुनिक टूलिंग के साथ कैसे इंटरैक्ट करता है।
numpy और pandas
NumPy और pandas ऑप्टिमाइज़्ड C कोड से चलते हैं, इसलिए reduce() के साथ उनकी कार्यक्षमता की नकल न करें। हालाँकि, डायनेमिक चरणों वाली पाइपलाइन्स के लिए reduce() अच्छा विकल्प है। उदाहरण के लिए, आप किसी array पर कई NumPy ट्रांसफ़ॉर्म्स को संयोजित कर सकते हैं।
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 ndarrayसंचालित द्वारा
पैरलल और डिस्ट्रीब्यूटेड कंप्यूटिंग फ्रेमवर्क्स
Reduce, पैरलल और डिस्ट्रीब्यूटेड कंप्यूटिंग फ्रेमवर्क्स का केंद्रीय हिस्सा है। ये सिस्टम डेटा सेट को पार्टिशनों में बाँटते हैं, उन पार्टिशनों को पैरलल में प्रोसेस करते हैं, फिर आंशिक परिणामों को एक उत्तर में संयोजित करते हैं। यही "combine" चरण रिडक्शन है।
क्लस्टर में रिडक्शन को सही ढंग से काम कराने के लिए, ये शर्तें पूरी हों:
सहसंयोजकता (Associativity)। ऑपरेशन का परिणाम समूहबद्ध करने के तरीके से स्वतंत्र रूप से समान होना चाहिए। इससे नेटवर्क भर में किसी भी क्रम में आंशिक परिणामों को जोड़ा जा सकता है।
पहचान तत्व (Identity element)। ऑपरेशन में ऐसा इनिशियलाइज़र होना चाहिए जो अंतिम परिणाम को प्रभावित न करे। इससे तब भी शुद्धता बनी रहती है जब पार्टिशन खाली हों या असमान आकार के हों।
यदि ये गुण नहीं होते, तो रिडक्शन्स धीमे (क्योंकि उन्हें सुरक्षित रूप से पैरलल नहीं किया जा सकता) या गलत (क्योंकि परिणाम मूल्यांकन क्रम पर निर्भर होते हैं) हो जाते हैं।
इस प्रक्रिया के लिए सहसंयोजी ऑपरेशन और स्पष्ट पहचान आवश्यक है। अन्यथा, कोड धीमा या परिणाम गलत होंगे।
स्टैटिक एनालिसिस टूल्स
स्टैटिक एनालाइज़र (जैसे mypy, Pyright, ruff, या bandit) ऐसा टूल है जो कोड को चलाए बिना जाँचता है। ये टूल्स बग पकड़ते हैं, स्टाइल नियम लागू करते हैं, और टाइप करेक्टनेस जाँचते हैं।
स्टैटिक एनालाइज़र्स को reduce() के साथ दिक्कत हो सकती है। फोल्डिंग प्रक्रिया में एक्यूमुलेटर का प्रकार तत्व के प्रकार से भिन्न हो सकता है, और टाइप इन्फ़रेंस उलझ सकता है।
यह कोड देखें।
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']संचालित द्वारा
भले ही यह कोड ठीक चलता है, एक स्टैटिक एनालाइज़र कुछ बातों पर आपत्ति कर सकता है।
इनिशियलाइज़र [] का एलिमेंट प्रकार अस्पष्ट है।
एनालाइज़र मान सकते हैं कि एक्यूमुलेटर और एलिमेंट प्रकार मेल खाते हैं।
कोड को मनुष्यों और एनालाइज़र्स के लिए स्पष्ट बनाने हेतु, टाइप हिंट्स जोड़ें।
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']संचालित द्वारा
अब रिड्यूसर स्पष्ट रूप से दिखाता है
एक्यूमुलेटर List[str] है।
हर एलिमेंट एक str है।
रिटर्न प्रकार एक list[str] है।
इन हिंट्स के साथ, स्टैटिक एनालाइज़र कोड की कड़ाई से जाँच कर सकते हैं।
निष्कर्ष
Reduce फंक्शनल प्रोग्रामिंग से आता है, जहाँ संग्रहों को एकल परिणाम में फोल्ड करना एक मुख्य विचार है। भले ही यह अब Python में प्रथम-श्रेणी का टूल नहीं है, इसका स्थान है। जब आपको लचीली पाइपलाइन्स, कस्टम फोल्ड्स, या ऐसे ऑपरेशंस चाहिए जो मौजूदा फ़ंक्शंस में साफ़-साफ़ न बैठें, तो reduce() एक शक्तिशाली टूल है। सावधानी से उपयोग करने पर, यह व्यापक Python इकोसिस्टम के साथ साफ़-सुथरे ढंग से एकीकृत होता है और सही परिस्थितियों में व्यावहारिक बना रहता है।
कुछ संबंधित Python लिंक जो आपके लिए उपयोगी हो सकते हैं।
Python का
reduce()फ़ंक्शन फंक्शनल प्रोग्रामिंग की दुनिया से आता है। फंक्शनल प्रोग्रामिंग (FP) एक प्रोग्रामिंग पैरेडाइम है जिसमें प्रोग्राम, अपरिवर्तनीय डेटा पर फ़ंक्शनों को लागू करके परिणाम बनाते हैं।इस शैली में एक आम पैटर्न "फोल्ड" है, जो किसी अनुक्रम को एकल परिणाम में समेट देता है। उदाहरण के लिए, संख्याओं की सूची [2, 4, 5, 3] को जोड़ के तहत क्रमिक चरणों से 14 में फोल्ड करना: [2, 4, 5, 3] → [6, 5, 3] → [11, 3] → 14.
reduce()इसी विचार को सामान्यीकृत करता है। यह एक बाइनरी ऑपरेशन को किसी iterable पर तब तक लागू करता है जब तक केवल परिणाम न बच जाए।इस लेख में, मैं Python के
reduce()के मुख्य तत्वों की पड़ताल करूँगा और कुछ व्यावहारिक उदाहरण दूँगा। यदि आपको Python की बुनियाद का पुनर्स्मरण चाहिए, तो मैं ये संसाधन देखने की सलाह देता हूँ:इतिहास
Python में फंक्शनल प्रोग्रामिंग के अन्य फ़ंक्शन भी हैं, जैसे
map()औरfilter().फंक्शनल संरचनाएँ जैसे
map()औरfilter()Python 1.0 में थीं। Guido van Rossum को वे पसंद नहीं थीं; उन्होंने बताया किreduce()पढ़ना मुश्किल है, और लगभग हमेशा एकforलूप अधिक पठनीय होता है। Python 3.0 में, PEP 3100 के अनुरूप, डेवलपर्स नेreduce()को बिल्ट-इन से हटा करfunctoolsमॉड्यूल में स्थानांतरित कर दिया।functoolsमें यह बदलाव मूलतः इसे एक विशिष्ट (niche) टूल के दर्जे पर ले आया।reduce() का उपयोग क्यों करें?
अधिकांश समय, बिल्ट-इन या लूप का उपयोग करना बेहतर विकल्प होता है। फिर भी, कुछ उपयोग मामलों में
reduce()एक अच्छा चुनाव है।Python reduce() कैसे काम करता है
आइए
reduce()की कार्यप्रणाली को समझें।reduce()का मूल फ़ंक्शन सिग्नेचर हैयह
reduce()दो अनिवार्य और एक वैकल्पिक तर्क लेता है।function: एक बाइनरी फ़ंक्शन जो दो तत्वों को कैसे संयोजित करना है, यह बताता है,iterable: वह अनुक्रम या iterable जिसे रिड्यूस करना है, जैसे सूची या ट्यूपल,initializer(वैकल्पिक): फ़ंक्शन को सीड करने के लिए प्रारंभिक मान।चरण-दर-चरण उदाहरण:
अंतिम परिणाम 13 है।
सरल reduce() उदाहरण
निम्नलिखित छोटे उदाहरण दिखाते हैं कि
reduce()का उपयोग कैसे किया जाता है।इनिशियलाइज़र
इनिशियलाइज़र के बिना,
reduce()iterable के पहले तत्व को अपने प्रारंभिक मान के रूप में लेता है। यदि iterable खाली है, तोreduce()एकTypeErrorफेंकता है। कोड को मज़बूत बनाने के लिए, खाली इनपुट पर व्यवहार परिभाषित करने हेतु एक इनिशियलाइज़र दें।इनिशियलाइज़र किसी खाली कंटेनर से भी परिणाम को सीड करते हैं। उदाहरण के लिए, आप शब्दों को अक्षरों की फ्लैट सूची में जोड़ सकते हैं।
Python और डेटा मैनिपुलेशन को और गहराई से जानने के लिए, ये विकल्प उपयोगी हैं।
रिड्यूसर परिभाषित करना
अब तक, हमने बाइनरी ऑपरेटर को परिभाषित करने के लिए लैम्ब्डा फ़ंक्शन का उपयोग किया है।
आप
operatorमॉड्यूल से ऑपरेटर भी उपयोग कर सकते हैं।operatorमॉड्यूल में सामान्य ऑपरेटरों और मेथड कॉल्स के फ़ंक्शन रूप हैं। उदाहरण के लिए, x + y के बजायoperator.add(x, y)। इससे आप बिना लैम्ब्डा लिखे, पहले से परिभाषित (और कुशल) ऑपरेटरों कोreduce() में पास कर सकते हैं।तीसरा विकल्प एक कस्टम फ़ंक्शन लिखना है। यह तब अच्छा होता है जब कोई पूर्वनिर्धारित ऑपरेटर न हो या फ़ंक्शन लैम्ब्डा के लिए बहुत जटिल हो।
उदाहरण के लिए, मान लें कि आप सूची से डुप्लिकेट हटाना चाहते हैं, लेकिन पहली बार दिखने के क्रम को बनाए रखना चाहते हैं। आप ऐसा रिड्यूसर परिभाषित कर सकते हैं जो किसी आइटम को तभी सूची में जोड़ता है जब वह पहले न आया हो।
डुप्लिकेट हटाने के और विचारों के लिए, यह ट्यूटोरियल देखें।
Python reduce() का प्रदर्शन
reduce()से प्रदर्शन से जुड़ी बातें आती हैं।फ़ंक्शन कॉल ओवरहेड
Python में
reduce()हमारा फ़ंक्शन iterable के हर तत्व के लिए एक बार कॉल करता है। यदि सूची में दस लाख आइटम हैं, तो इसका अर्थ है दस लाख फ़ंक्शन कॉल—हर कॉल एक फ़्रेम बनाती है, आर्ग्युमेंट सँभालती है, और रेफरेंस काउंट अपडेट करती है। यह काफ़ी ओवरहेड जोड़ता है।इसके विपरीत,
sumजैसा बिल्ट-इन एक ही बार C फ़ंक्शन में कॉल करता है और लाख जोड़ C लूप के भीतर करता है। यह फ़र्क बिल्ट-इन को कई गुना तेज़ बना सकता है।कैश लोकैलिटी और CPU दक्षता से जुड़ी समस्याएँ
Reduce को कैश लोकैलिटी और CPU दक्षता से भी नुकसान होता है।
आधुनिक CPU प्रति सेकंड कई अरब निर्देश चला सकता है, लेकिन RAM एक्सेस उससे कई गुना धीमा है। इसकी भरपाई के लिए, आधुनिक CPU में कैश (L1, L2, L3) होते हैं जो तेज़ एक्सेस के लिए डेटा स्टोर करते हैं।
ये कैश दो पैटर्न का लाभ उठाते हैं।
इसके विपरीत,
reduce()का हर चरण अगले तत्व को ढूँढने के लिए पॉइंटर चेज़िंग और Python फ़ंक्शन कॉल्स में जाता है, जो लोकैलिटी तोड़ते हैं और CPU को स्टॉल करते हैं। बिल्ट-इन्स और वेक्टराइज़्ड फ़ंक्शंस इस समस्या से बचते हैं क्योंकि वे कसे हुए C लूप्स में चलते हैं।आदर्श और कुशल Python कोड लिखने के लिए पुनर्स्मरण हेतु ये देखें:
Python reduce() के विकल्प
बिल्ट-इन्स:
reduce()का ओवरहेड नहीं लगता। बड़े इनपुट पर यह गति लाभ और बढ़ जाता है।sum,min) इसलिए उनका आशय स्पष्ट होता है।reduce()कॉल में आपको फोल्ड की जा रही फ़ंक्शन को समझना पड़ता है।लूप्स:
reduce()से तेज़ चलते हैं।reduce()से अधिक पठनीय होते हैं।reduce()आपको एक फंक्शनल स्टेटमेंट पार्स करने पर मजबूर करता है, जबकि लूप अधिक पाइथॉनिक है।itertools.accumulate()Python की
itertoolsलाइब्रेरी मेंcount(),product()औरcombinations()जैसे परफ़ॉर्मेंट इटरेटर्स का संग्रह है। एक उपयोगीitertoolsफ़ंक्शन हैitertools.accumulate()। यहreduce()की तरह किसी iterable पर फ़ंक्शन को फोल्ड करता है। हालाँकि,accumulate() केवल अंतिम परिणाम नहीं, बल्कि मध्यवर्ती मान भी संग्रहीत करता है।उदाहरण के लिए,
Accumulate तब उपयोगी है जब आपको रनिंग टोटल्स या न्यूनतम/अधिकतम चाहिए। उदाहरण के लिए, आप महीने-दर-महीने अधिकतम तापमान जानना चाह सकते हैं।
reduce() का उपयोग करते समय सामान्य गलतियाँ
जब आप
reduce()का उपयोग करें, तो निम्नलिखित बातों का ध्यान रखें।reduce()को तभी बचाएँ जब वास्तव में उसकी ज़रूरत हो।reduce()को ज़बरदस्ती न फिट करें जहाँ जेनरेटर या स्ट्रीमिंग तरीका अधिक कुशल होगा।operatorमॉड्यूल से फ़ंक्शंस उपयोग करें। खासकर गैर-सहसंयोजी (non-associative) ऑपरेशंस के साथ लैम्ब्डा स्पष्टता को नुकसान पहुँचा सकते हैं।Python reduce() सर्वोत्तम प्रथाएँ और दिशानिर्देश
किसी भी टूल की तरह,
reduce()के साथ भी सर्वोत्तम प्रथाएँ हैं। यदि आपने तय कर लिया है किreduce()सही टूल है, तो इसके उपयोग के लिए कुछ दिशानिर्देश यहाँ हैं।पहले रिड्यूसर डिज़ाइन करें
minके लिएmath.inf, औरset unionके लिएset()। इससे कोड मज़बूत रहता है औरTypeErrorsसे मुक्त रहता है।इसे सरल और पठनीय रखें
दस्तावेज़ करें
परीक्षण करें
प्रदर्शन मॉनिटर करें
Python reduce() के उन्नत उपयोग और वास्तविक-life केस
खामियों के बावजूद, ऐसा लग सकता है कि
reduce()का कोई वास्तविक मूल्य नहीं है। इसके विपरीत,reduce()के कई उपयोगी उपयोग मामले हैं।नेस्टेड संरचनाओं का प्रोसेसिंग
Reduce, JSON ऑब्जेक्ट्स जैसी नेस्टेड डेटा संरचनाओं को ट्रैवर्स करने का एक साफ़ तरीका देता है, जिसमें कुंजियों की शृंखला को क्रमिक लुकअप्स में फोल्ड किया जाता है।
यहाँ
reduce()का उपयोग समझ में आता है। JSON गहराई से नेस्टेड है:user → profile → address → city। मैन्युअली लुकअप्स चेन करने के बजाय, पथ को कुंजियों की सूची के रूप में दर्शाएँ। फिरreduce(operator.getitem, path, data)का उपयोग कर उसे ट्रैवर्स करें। इससे कोड जेनेरिक, पठनीय और पुन: प्रयोज्य बनता है।डेटा प्रोसेसिंग पाइपलाइन्स
Reduce, डेटा को रूपांतरणों की एक शृंखला से गुज़ार कर पाइपलाइन्स चला सकता है। हर फ़ंक्शन एक चरण सँभालता है, और पाइपलाइन उन्हीं को क्रम में लागू करने से बनती है। यहाँ एक छोटा उदाहरण है जो किसी NLP मॉडल में भेजने से पहले पाठ को प्रीप्रोसेस करता है।
जटिल एप्लिकेशनों में त्रुटि हैंडलिंग
आइए नेस्टेड JSON वाले उदाहरण पर लौटें। फिलहाल, सीधे
reduce(operator.getitem, …)कॉल करने पर यदि कोई कुंजी गायब हो या non-dict मिल जाए तोKeyErrorयाTypeErrorआता है। कोड को सुरक्षित बनाने के लिए, एक सहायक फ़ंक्शन परिभाषित करें जोoperator.getitemको try/except में रैप करे और त्रुटि पर डिफ़ॉल्ट मान लौटाए।सहायक फ़ंक्शन का एक विकल्प यहाँ है।
अब, अनरैप्ड
reduce()के बजाय हमारे नए फ़ंक्शन का उपयोग करके उदाहरण लुकअप्स बदलेंmap() और filter() के साथ बहु-चरणीय डेटा रूपांतरण
आप
reduce()को अन्य फंक्शनल टूल्स, जैसेmap()औरfilter()के साथ जोड़कर बहु-चरणीय डेटा रूपांतरण बना सकते हैं। यहाँ हमारा पहले वाला NLP प्रीप्रोसेसिंग पाइपलाइन फंक्शनल रूप में है।रूपांतरण ये हैं:
"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"
फंक्शनल प्रोग्रामिंग और वेक्टराइज़ेशन विचारों को आगे जानने के लिए, हम ये DataCamp लेख सुझाते हैं।
आधुनिक Python इकोसिस्टम के साथ एकीकरण
reduce()का अच्छा उपयोग करने के लिए यह समझना उपयोगी है कि यह आधुनिक Python इकोसिस्टम में कैसे फिट होता है। आइए देखें कि यह NumPy और pandas के साथ कैसे बैठता है, यह पैरलल और डिस्ट्रीब्यूटेड सिस्टम्स की रीढ़ कैसे है, और यह mypy जैसे स्टैटिक एनालाइज़र्स सहित आधुनिक टूलिंग के साथ कैसे इंटरैक्ट करता है।numpy और pandas
NumPy और pandas ऑप्टिमाइज़्ड C कोड से चलते हैं, इसलिए
reduce()के साथ उनकी कार्यक्षमता की नकल न करें। हालाँकि, डायनेमिक चरणों वाली पाइपलाइन्स के लिएreduce()अच्छा विकल्प है। उदाहरण के लिए, आप किसी array पर कई NumPy ट्रांसफ़ॉर्म्स को संयोजित कर सकते हैं।पैरलल और डिस्ट्रीब्यूटेड कंप्यूटिंग फ्रेमवर्क्स
Reduce, पैरलल और डिस्ट्रीब्यूटेड कंप्यूटिंग फ्रेमवर्क्स का केंद्रीय हिस्सा है। ये सिस्टम डेटा सेट को पार्टिशनों में बाँटते हैं, उन पार्टिशनों को पैरलल में प्रोसेस करते हैं, फिर आंशिक परिणामों को एक उत्तर में संयोजित करते हैं। यही "combine" चरण रिडक्शन है।
क्लस्टर में रिडक्शन को सही ढंग से काम कराने के लिए, ये शर्तें पूरी हों:
यदि ये गुण नहीं होते, तो रिडक्शन्स धीमे (क्योंकि उन्हें सुरक्षित रूप से पैरलल नहीं किया जा सकता) या गलत (क्योंकि परिणाम मूल्यांकन क्रम पर निर्भर होते हैं) हो जाते हैं।
इस प्रक्रिया के लिए सहसंयोजी ऑपरेशन और स्पष्ट पहचान आवश्यक है। अन्यथा, कोड धीमा या परिणाम गलत होंगे।
स्टैटिक एनालिसिस टूल्स
स्टैटिक एनालाइज़र (जैसे mypy, Pyright, ruff, या bandit) ऐसा टूल है जो कोड को चलाए बिना जाँचता है। ये टूल्स बग पकड़ते हैं, स्टाइल नियम लागू करते हैं, और टाइप करेक्टनेस जाँचते हैं।
स्टैटिक एनालाइज़र्स को
reduce()के साथ दिक्कत हो सकती है। फोल्डिंग प्रक्रिया में एक्यूमुलेटर का प्रकार तत्व के प्रकार से भिन्न हो सकता है, और टाइप इन्फ़रेंस उलझ सकता है।यह कोड देखें।
भले ही यह कोड ठीक चलता है, एक स्टैटिक एनालाइज़र कुछ बातों पर आपत्ति कर सकता है।
कोड को मनुष्यों और एनालाइज़र्स के लिए स्पष्ट बनाने हेतु, टाइप हिंट्स जोड़ें।
अब रिड्यूसर स्पष्ट रूप से दिखाता है
List[str]है।strहै।list[str]है।इन हिंट्स के साथ, स्टैटिक एनालाइज़र कोड की कड़ाई से जाँच कर सकते हैं।
निष्कर्ष
Reduce फंक्शनल प्रोग्रामिंग से आता है, जहाँ संग्रहों को एकल परिणाम में फोल्ड करना एक मुख्य विचार है। भले ही यह अब Python में प्रथम-श्रेणी का टूल नहीं है, इसका स्थान है। जब आपको लचीली पाइपलाइन्स, कस्टम फोल्ड्स, या ऐसे ऑपरेशंस चाहिए जो मौजूदा फ़ंक्शंस में साफ़-साफ़ न बैठें, तो
reduce()एक शक्तिशाली टूल है। सावधानी से उपयोग करने पर, यह व्यापक Python इकोसिस्टम के साथ साफ़-सुथरे ढंग से एकीकृत होता है और सही परिस्थितियों में व्यावहारिक बना रहता है।कुछ संबंधित Python लिंक जो आपके लिए उपयोगी हो सकते हैं।