Python में फ़ंक्शन: def, return, आर्गुमेंट, *args और **kwargs, lambda और टाइप एनोटेशन
Python में फ़ंक्शन एक नाम वाला कोड-खंड है, जिसे def से घोषित किया जाता है, नाम और कोष्ठक से बुलाया जाता है और return से परिणाम लौटाया जाता है; बिना return के फ़ंक्शन None लौटाता है। आर्गुमेंट क्रम से या नाम से दिए जाते हैं, *args बाक़ी पोज़िशनल मानों को टपल में और **kwargs नाम वाले मानों को डिक्शनरी में इकट्ठा करता है, और lambda एक व्यंजन वाला छोटा बेनाम फ़ंक्शन है।
Python में फ़ंक्शन एक नाम वाला कोड-खंड है, जिसे अलग-अलग इनपुट के साथ कितनी भी बार बुलाया जा सकता है। इसे def से घोषित किया जाता है, पैरामीटर से मान लिए जाते हैं और return से परिणाम लौटाया जाता है। लंबी स्क्रिप्ट को समझने और जाँचने लायक चरणों में बाँटने का मुख्य तरीका फ़ंक्शन ही है।
def से फ़ंक्शन घोषित करना
फ़ंक्शन की घोषणा def से शुरू होती है, फिर नाम, कोष्ठक में पैरामीटर और कोलन आते हैं। बॉडी चार स्पेस के इंडेंटेशन से लिखी जाती है — Python में इंडेंटेशन सजावट नहीं, सिंटैक्स का हिस्सा है।
def greet(name):
"""उपयोगकर्ता के लिए अभिवादन लौटाता है।"""
return f"नमस्ते, {name}!"
print(greet("अनीता")) # नमस्ते, अनीता!
शीर्षक के ठीक बाद तीन उद्धरण चिह्नों की स्ट्रिंग docstring है, यानी फ़ंक्शन का दस्तावेज़। उसे help(greet) और एडिटर के सुझाव दिखाते हैं। फ़ंक्शन के नाम PEP 8 के अनुसार snake_case में लिखे जाते हैं: add_expense, get_rate।
def greet(name): वाली पंक्ति को फ़ंक्शन का सिग्नेचर कहते हैं: नाम और पैरामीटर की सूची (और एनोटेशन, अगर हों)। सिग्नेचर देखकर पाठक समझ जाता है कि फ़ंक्शन कैसे बुलाना है, बिना बॉडी पढ़े।
Python में return क्या करता है
return फ़ंक्शन को समाप्त करता है और बुलाने वाले को मान लौटाता है। return के बाद का कोड नहीं चलता। अगर return नहीं है, या बिना मान के लिखा है, तो फ़ंक्शन None लौटाता है।
def area(w, h):
w * h # परिणाम निकला, पर फेंक दिया गया
print(area(3, 4)) # None
def area(w, h):
return w * h
print(area(3, 4)) # 12
छूटा हुआ return नए छात्रों की सबसे आम गलतियों में है: फ़ंक्शन «काम» करता दिखता है, पर None लौटाता है, और गड़बड़ी एक पंक्ति आगे जाकर दिखती है। एक से ज़्यादा मान भी लौटाए जा सकते हैं — असल में यह एक टपल होता है, जिसे आसानी से अनपैक किया जा सकता है:
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([4, 1, 9])
print(low, high) # 1 9
जल्दी return करने से अनावश्यक नेस्टिंग घटती है: पहले ख़ास स्थितियाँ निपटाएँ, फिर मुख्य तर्क लिखें।
def discount(price, is_member):
if price <= 0:
return 0
if not is_member:
return price
return price * 0.9
Python फ़ंक्शन के आर्गुमेंट: पोज़िशनल और नाम वाले
पैरामीटर वे नाम हैं जो फ़ंक्शन की घोषणा में हैं, और आर्गुमेंट वे मान हैं जो बुलाते समय दिए जाते हैं। आर्गुमेंट क्रम से (पोज़िशनल) या नाम से (कीवर्ड) दिए जा सकते हैं। नाम वाले आर्गुमेंट कॉल को खुद समझाने लायक बनाते हैं, और तब क्रम मायने नहीं रखता।
def add_expense(title, amount, category="अन्य"):
return {"title": title, "amount": amount, "category": category}
add_expense("कॉफ़ी", 250) # क्रम से
add_expense(amount=250, title="कॉफ़ी") # नाम से
add_expense("टैक्सी", 600, category="यात्रा") # मिलाकर
डिफ़ॉल्ट मान वाला पैरामीटर वैकल्पिक है। नियम: डिफ़ॉल्ट वाले पैरामीटर ज़रूरी पैरामीटर के बाद आते हैं, और कॉल में पोज़िशनल आर्गुमेंट नाम वाले आर्गुमेंट से पहले आते हैं। वरना Python SyntaxError देगा।
सावधानी: बदले जा सकने वाला डिफ़ॉल्ट मान
डिफ़ॉल्ट मान एक बार बनता है — जब def वाली पंक्ति चलती है, हर कॉल पर नहीं। अगर वह लिस्ट या डिक्शनरी हो, तो वह सभी कॉल में साझा रहेगा।
def add_item(item, acc=[]):
acc.append(item)
return acc
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — लिस्ट को पिछली कॉल याद है
सही तरीका है डिफ़ॉल्ट None रखना और अंदर नया ऑब्जेक्ट बनाना:
def add_item(item, acc=None):
if acc is None:
acc = []
acc.append(item)
return acc
*args और **kwargs: कितने भी आर्गुमेंट
पैरामीटर से पहले तारा लगाने पर सारे बचे पोज़िशनल आर्गुमेंट एक टपल (tuple) में इकट्ठा होते हैं, और दो तारे लगाने पर बचे नाम वाले आर्गुमेंट एक डिक्शनरी (dict) में। नाम args और kwargs केवल परंपरा हैं; असली महत्व तारों का है।
def total(*args):
return sum(args)
print(total(100, 250, 40)) # 390
def make_user(name, **kwargs):
user = {"name": name}
user.update(kwargs)
return user
print(make_user("अनीता", city="पुणे", age=30))
# {'name': 'अनीता', 'city': 'पुणे', 'age': 30}
वही तारे उल्टी दिशा में भी काम करते हैं — कॉल करते समय वे सूची या डिक्शनरी को आर्गुमेंट में खोल देते हैं:
values = [3, 4]
print(pow(*values)) # 81
opts = {"sep": " | ", "end": "\n"}
print("a", "b", **opts) # a | b
सिर्फ़ पोज़िशनल और सिर्फ़ नाम वाले पैरामीटर
पैरामीटर-सूची में एक तिरछी रेखा / और तारा * रुकावट का काम करते हैं: / से पहले के आर्गुमेंट सिर्फ़ क्रम से दिए जा सकते हैं (यह सिंटैक्स Python 3.8 में आया, PEP 570), और * के बाद के सिर्फ़ नाम से।
def convert(amount, /, *, rate, currency="USD"):
return round(amount / rate, 2), currency
convert(100, rate=90.5) # (1.1, 'USD')
convert(amount=100, rate=90.5) # TypeError: amount सिर्फ़ पोज़िशनल है
convert(100, 90.5) # TypeError: rate सिर्फ़ नाम से दिया जाए
सिर्फ़ नाम वाले पैरामीटर convert(100, 90.5, "EUR") जैसी उलझी कॉल से बचाते हैं, जहाँ साफ़ नहीं होता कि कौन-सा मान क्या है।
स्कोप: LEGB नियम
Python वेरिएबल का नाम इस क्रम में खोजता है: Local (फ़ंक्शन के भीतर) → Enclosing (बाहरी फ़ंक्शन में) → Global (मॉड्यूल स्तर पर) → Built-in (len जैसे बिल्ट-इन नाम)। फ़ंक्शन के भीतर ग्लोबल वेरिएबल पढ़ा जा सकता है, पर उसे नया मान देने के लिए global घोषणा चाहिए।
limit = 1000
def check(amount):
return amount > limit # ग्लोबल को पढ़ना — ठीक है
def raise_limit():
global limit
limit = 2000 # global के बिना स्थानीय वेरिएबल बन जाता
व्यवहार में global से बचना बेहतर है: जो फ़ंक्शन बाहरी स्थिति बदलता है, उसे जाँचना कठिन होता है। भरोसेमंद तरीका है मान को आर्गुमेंट के रूप में लेना और परिणाम लौटाना।
नेस्टेड फ़ंक्शन और क्लोज़र
फ़ंक्शन को दूसरे फ़ंक्शन के भीतर भी घोषित किया जा सकता है। भीतरी फ़ंक्शन बाहरी फ़ंक्शन के वेरिएबल देखता है, भले ही बाहरी फ़ंक्शन ख़त्म हो चुका हो — इसी को क्लोज़र कहते हैं। ऐसे वेरिएबल को बदलने के लिए nonlocal लिखा जाता है।
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter()
print(c(), c(), c()) # 1 2 3
क्लोज़र डेकोरेटर और फ़ंक्शन-फ़ैक्टरी की नींव हैं।
फ़ंक्शन भी एक ऑब्जेक्ट है
Python में फ़ंक्शन एक साधारण ऑब्जेक्ट है: उसे वेरिएबल में रखा जा सकता है, दूसरे फ़ंक्शन को दिया जा सकता है, किसी फ़ंक्शन से लौटाया जा सकता है, डिक्शनरी में रखा जा सकता है। उसके गुण होते हैं: __name__, __doc__, __defaults__, __annotations__।
def double(x):
return x * 2
f = double
print(f(5), f.__name__) # 10 double
print(list(map(double, [1, 2, 3]))) # [2, 4, 6]
Python में lambda: बेनाम फ़ंक्शन
Lambda एक छोटा फ़ंक्शन है, जिसका कोई नाम नहीं होता और बॉडी में सिर्फ़ एक व्यंजन होता है। व्यंजन का परिणाम अपने-आप लौटता है; return लिखना मना है।
square = lambda x: x ** 2
print(square(4)) # 16
expenses = [("कॉफ़ी", 250), ("टैक्सी", 600), ("भोजन", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses) # [('टैक्सी', 600), ('भोजन', 450), ('कॉफ़ी', 250)]
print(list(filter(lambda n: n % 2 == 0, range(10)))) # [0, 2, 4, 6, 8]
Lambda छोटे आर्गुमेंट के रूप में sorted, max, map, filter के साथ ठीक है। लेकिन जैसे ऊपर square को वेरिएबल में रखना PEP 8 के अनुसार अच्छा नहीं माना जाता: जिस फ़ंक्शन को नाम चाहिए, उसके लिए def लिखें — तब ट्रेसबैक में सामान्य __name__ होगा और docstring की जगह भी।
| def | lambda | |
|---|---|---|
| नाम | है | नहीं (<lambda>) |
| बॉडी | कितने भी स्टेटमेंट | एक व्यंजन |
| return | स्पष्ट | अपने-आप |
| Docstring, एनोटेशन | हाँ | नहीं |
| कहाँ ठीक है | हर जगह | छोटी key या कॉलबैक |
टाइप एनोटेशन: संकेत, जाँच नहीं
एनोटेशन बताते हैं कि पैरामीटर और परिणाम किस प्रकार के होने चाहिए। Python चलते समय इन्हें नहीं जाँचता: x: int वाला फ़ंक्शन भी स्ट्रिंग स्वीकार कर लेगा। जाँच बाहरी टूल करते हैं — mypy, pyright, ty — और एडिटर, जो चलाने से पहले ही गलती दिखा देता है।
def add_expense(title: str, amount: float, category: str = "अन्य") -> dict[str, object]:
return {"title": title, "amount": amount, "category": category}
def area(w: float, h: float) -> float:
w * h # mypy: Missing return statement
इंस्टॉल किए बिना जाँच uvx mypy . से चलती है। ऊपर का उदाहरण दिखाता है कि एनोटेशन कितने काम के हैं: -> float के साथ छूटा return विश्लेषक तुरंत पकड़ लेता है।
Python 3.12 (PEP 695) से जेनेरिक फ़ंक्शन बिना TypeVar के लिखे जाते हैं: टाइप पैरामीटर वर्गाकार ब्रैकेट में आता है, और टाइप उपनाम शब्द type से घोषित होता है।
def first[T](xs: list[T]) -> T:
return xs[0]
type Money = int | float
Python 3.14 (PEP 649) में एनोटेशन आलसी (lazy) हो गए: वे पहली बार इस्तेमाल होने पर मूल्यांकित होते हैं, इसलिए बाद में घोषित क्लास का नाम बिना उद्धरण चिह्न के लिखा जा सकता है, और from __future__ import annotations की ज़रूरत नहीं रही।
«नहीं दिया गया» या «None दिया गया»: Python 3.15 का sentinel
डिफ़ॉल्ट None से यह पता नहीं चलता कि आर्गुमेंट नहीं दिया गया, या जानबूझकर None दिया गया। सालों तक इसका हल हाथ से बनाए गए मार्कर से निकाला जाता रहा:
_MISSING = object()
def get_rate(currency, default=_MISSING):
rates = {"USD": 90.5}
if currency in rates:
return rates[currency]
if default is _MISSING:
raise KeyError(currency)
return default
Python 3.15 में बिल्ट-इन फ़ंक्शन sentinel आया है (PEP 661): MISSING = sentinel("MISSING") एक अनूठा मार्कर बनाता है, जिसका repr साफ़ दिखता है, कॉपी करने पर भी वही रहता है, pickle से सेव हो सकता है, और float | MISSING जैसे एनोटेशन में काम आता है। पुराने वर्ज़न पर object() वाला तरीका चलता रहता है।
फ़ंक्शन की आम गलतियाँ
- return भूल गए — फ़ंक्शन
Noneलौटाता है। - कोष्ठक भूल गए —
print(greet)फ़ंक्शन-ऑब्जेक्ट छापेगा, कॉल का परिणाम नहीं। - बदले जा सकने वाला डिफ़ॉल्ट मान — सभी कॉल में साझा लिस्ट।
- आर्गुमेंट की गलत संख्या —
TypeError: greet() missing 1 required positional argument: 'name'। - बिल्ट-इन नाम जैसा फ़ंक्शन नाम —
listयाsumनाम का फ़ंक्शन बिल्ट-इन को ढक देगा।
निष्कर्ष
Python में फ़ंक्शन def से बनते हैं, परिणाम return से लौटाते हैं और आर्गुमेंट क्रम से या नाम से लेते हैं। *args और **kwargs लचीलापन देते हैं, / और * कॉल को सख़्त बनाते हैं, lambda एक बार के फ़ंक्शन को छोटा करता है, और टाइप एनोटेशन mypy के साथ मिलकर गलतियाँ चलाने से पहले पकड़ते हैं। कोर्स में यह विषय व्यवहार में सिखाया जाता है: ख़र्च ट्रैकर को एनोटेशन वाले फ़ंक्शनों में बाँटा जाता है और mypy से जाँचा जाता है।
अक्सर पूछे जाने वाले प्रश्न
पैरामीटर और आर्गुमेंट में क्या फ़र्क है?
पैरामीटर फ़ंक्शन की घोषणा में दिया गया नाम है, और आर्गुमेंट कॉल के समय दिया गया असली मान। def greet(name) में name पैरामीटर है, और greet("अनीता") में "अनीता" आर्गुमेंट।
क्या फ़ंक्शन को घोषित होने से पहले बुलाया जा सकता है?
मॉड्यूल स्तर पर नहीं: def वाली पंक्ति कॉल से पहले चलनी चाहिए, वरना NameError आएगा। लेकिन किसी दूसरे फ़ंक्शन के भीतर बाद में घोषित फ़ंक्शन बुलाया जा सकता है, बशर्ते कॉल तब हो जब वह घोषित हो चुका हो।
फ़ंक्शन एक से ज़्यादा मान कैसे लौटाता है?
औपचारिक रूप से एक ही मान, पर वह मान टपल हो सकता है: return a, b दो तत्वों का टपल लौटाता है, जिसे x, y = f() से अनपैक करते हैं।
क्या टाइप एनोटेशन लिखना ज़रूरी है?
नहीं, Python इनके बिना भी चलता है। पर दूसरों द्वारा पढ़े जाने वाले कोड में एनोटेशन रखरखाव आसान करते हैं और mypy या pyright को चलाने से पहले गलतियाँ पकड़ने देते हैं।
क्या lambda में if लिखा जा सकता है?
सिर्फ़ शर्तीय व्यंजन: lambda x: "सम" if x % 2 == 0 else "विषम"। lambda के भीतर स्टेटमेंट, लूप और असाइनमेंट मान्य नहीं हैं।
यह विषय “Python 3.15: от новичка до профи” कोर्स में अभ्यास के साथ विस्तार से समझाया गया है।
कोर्स पर जाएँ