फ़ंक्शन आर्ग्युमेंट — नाम, डिफ़ॉल्ट और स्टार
नाम से आर्ग्युमेंट भेजना, डिफ़ॉल्ट वैल्यू और उनके क्रम का नियम, *args और **kwargs, और कॉल करते समय लिस्ट या डिक्शनरी को अनपैक करना।
आप कैसे सीखना चाहते हैं?
वह समस्या जिसे हम हल कर रहे हैं
पिछले अध्याय के फ़ंक्शन एक ही तरह से कॉल किए जा सकते थे: जितने पैरामीटर, ठीक उतने आर्ग्युमेंट, ठीक उसी क्रम में।
असल में यह काफ़ी नहीं है। ऑर्डर वाला एक फ़ंक्शन सोचिए:
def order(item, quantity):
return f"{quantity} x {item}"
print(order("pen", 3))
print(order(3, "pen"))3 x pen
pen x 3दूसरी लाइन ने कोई एरर नहीं दिया — बस बेमतलब जवाब दिया। Python आर्ग्युमेंट का टाइप नहीं जाँचता: यहाँ एक स्ट्रिंग और एक नंबर ने जगह बदल ली, और f-string दोनों को प्रिंट कर सकती है, इसलिए कहीं कुछ नहीं अटका। क्रम उल्टा हो गया है या नहीं, यह जानने का Python के पास कोई तरीका नहीं है।
और अगर लगभग सारे ऑर्डर एक ही चीज़ के हों? हर बार 1 लिखना पड़ेगा। फ़ंक्शन में चार वैकल्पिक सेटिंग हों? हर बार चारों लिखनी पड़ेंगी, उन्हें भी जिन्हें आप बदल नहीं रहे।
आज का अध्याय आर्ग्युमेंट भेजने के बाकी तरीकों के बारे में है — नाम से भेजना, डिफ़ॉल्ट वैल्यू, और अनगिनत आर्ग्युमेंट।
इस अध्याय के अंत तक आप कर पाएंगे
- नाम से आर्ग्युमेंट भेजना, और बताना कि कब ऐसा लिखना ही चाहिए
- डिफ़ॉल्ट वैल्यू देना, और उनके क्रम का नियम मानना
- कुछ पैरामीटर को ऐसा बनाना कि वे सिर्फ़ नाम से भेजे जा सकें
*argsसे कितने भी आर्ग्युमेंट लेना**kwargsसे किसी भी नाम के आर्ग्युमेंट लेना- एक लिस्ट या डिक्शनरी को खोलकर आर्ग्युमेंट के रूप में भेजना
पहले से क्या आना चाहिए: फ़ंक्शन — एक फ़ैसला, एक नाम।
नाम से भेजना
क्रम याद रखने की जगह नाम ही लिख दें:
def order(item, quantity):
return f"{quantity} x {item}"
print(order(quantity=3, item="pen"))3 x penनाम दें तो क्रम मायने नहीं रखता — quantity पहले लिखा होने के बावजूद सही जगह पहुँचा।
कब लिखें? जब आर्ग्युमेंट देखकर समझ न आए। order("pen", 3) पढ़ते ही समझ आता है। लेकिन send(True, False) पढ़कर कुछ समझ नहीं आता — send(retry=True, verbose=False) लिखें तो आता है। नियम के तौर पर: True/False या अकेला नंबर भेजते समय नाम लिखें।
डिफ़ॉल्ट वैल्यू
def order(item, quantity=1):
return f"{quantity} x {item}"
print(order("pen"))
print(order("pen", 5))
print(order("pen", quantity=5))1 x pen
5 x pen
5 x penquantity=1 कह रहा है “न दिया जाए तो एक मान लो”। आम मामला छोटा हो जाता है, और खास मामला भी लिखा जा सकता है।
एक नियम मानना ही होगा: डिफ़ॉल्ट वाला पैरामीटर हमेशा बाद में आता है।
def order(quantity=1, item):
return itemSyntaxError: parameter without a default follows parameter with a defaultवजह सोचें तो समझ आती है: order("pen") लिखें तो Python कैसे समझेगा कि "pen" किसके लिए है? ज़रूरी वाले पहले हों तो यह दुविधा नहीं रहती।
कितने भी आर्ग्युमेंट — *args
def total(*prices):
return sum(prices)
print(total(15, 60))
print(total(15, 60, 850))
print(total())75
925
0*prices का स्टार (*) कह रहा है “बाकी सारे positional आर्ग्युमेंट यहाँ इकट्ठा करो”। कितने आएँगे यह पहले से तय करने की ज़रूरत नहीं, और एक भी न आए तो कोई समस्या नहीं।
इकट्ठा की गई चीज़ असल में क्या है?
def show(*things):
print(type(things))
print(things)
show(1, "two")<class 'tuple'>
(1, 'two')एक टपल — 14 नंबर अध्याय वाला वही टपल। तर्क अच्छा है: आर्ग्युमेंट की संख्या कॉल के समय ही तय हो जाती है और उसके बाद बदलनी नहीं चाहिए, इसलिए न बदलने वाला कंटेनर ही सही बैठता है।
pricesनाम ज़रूरी नहीं है। चलन वाला नाम*argsहै, और ज़्यादातर कोड में वही दिखेगा। लेकिन मतलब वाला नाम हो तो वही बेहतर है —*pricesपढ़कर समझ आता है कि अंदर क्या आ रहा है,*argsसे नहीं।
किसी भी नाम के आर्ग्युमेंट — **kwargs
def describe(**fields):
print(type(fields))
for key, value in fields.items():
print(f"{key}: {value}")
describe(item="pen", quantity=3)<class 'dict'>
item: pen
quantity: 3दो स्टार नाम से भेजे गए आर्ग्युमेंट इकट्ठा करते हैं, और नतीजा एक डिक्शनरी होता है — नाम (key) और वैल्यू की जोड़ियाँ, ठीक वैसा जैसा होना चाहिए।
एक स्टार यानी टपल, दो यानी डिक्शनरी। यह जोड़ी याद रखें तो बाकी अंदाज़ा लगाया जा सकता है।
सिर्फ़ नाम से भेजे जा सकने वाले पैरामीटर
*args के बाद जो पैरामीटर आता है, उसे position से भेजने का कोई तरीका नहीं है — क्योंकि *args बाकी सारे positional आर्ग्युमेंट खुद ले लेता है। इसलिए उसे सिर्फ़ नाम से भेजा जा सकता है। इसे keyword-only पैरामीटर कहते हैं:
def total(*prices, discount=0.0):
return sum(prices) * (1 - discount)
print(total(100, 50))
print(total(100, 50, discount=0.1))
print(total(100, 50, 0.1))150.0
135.0
150.1तीसरी लाइन पर ध्यान दें। 0.1 छूट के रूप में नहीं गया — वह एक और कीमत बनकर prices में चला गया, और बिना किसी एरर के गलत जवाब आया। discount भेजना हो तो discount=0.1 लिखना ही पड़ेगा।
*args न चाहिए तब भी किसी पैरामीटर को सिर्फ़-नाम-वाला बनाया जा सकता है: पैरामीटर की लिस्ट में अकेला एक * रख दें। उसके बाद वाले सब keyword-only हैं:
def send(message, *, retry=False):
return f"{message} (retry={retry})"
print(send("hi", retry=True))
print(send("hi", True))hi (retry=True)
TypeError: send() takes 1 positional argument but 2 were given(दूसरी लाइन से पहले Python traceback की कुछ लाइनें भी प्रिंट करता है; यहाँ सिर्फ़ आखिरी लाइन दिखाई गई है।)
True/False जैसी सेटिंग के लिए यह बहुत काम का है: कॉल करने वाले को retry=True लिखना ही पड़ता है, इसलिए कॉल पढ़ते ही समझ आ जाता है कि True किस बारे में है।
क्रम तय है
सब एक साथ लिखें तो पूरा नियम ऐसा है:
- पहले सामान्य पैरामीटर। इनमें बिना डिफ़ॉल्ट वाले पहले, डिफ़ॉल्ट वाले बाद में।
- फिर
*args(या अकेला एक*)। - फिर keyword-only पैरामीटर — डिफ़ॉल्ट के साथ या बिना, किसी भी क्रम में।
- सबसे आखिर में
**kwargs।
def f(a, b=1, *args, c, d=2, **kwargs):
print(a, b, args, c, d, kwargs)
f(10, c=3)
f(10, 20, 30, 40, c=3, e=5)10 1 () 3 2 {}
10 20 (30, 40) 3 2 {'e': 5}c का कोई डिफ़ॉल्ट नहीं है, फिर भी वह डिफ़ॉल्ट वाले b=1 के बाद आया है, और यह सही है — क्योंकि *args के बाद वाले सब नाम से आते हैं, इसलिए कौन-सी वैल्यू कहाँ जाएगी इसमें कोई दुविधा नहीं। “डिफ़ॉल्ट वाले बाद में” वाला नियम सिर्फ़ *args से पहले वाले हिस्से पर लागू होता है।
पहली नज़र में यह झंझट लगता है, लेकिन वजह सीधी है: Python को जानना होता है कि कौन-सा आर्ग्युमेंट कहाँ जाएगा, और *args क्योंकि “बाकी सब” ले लेता है, उसके बाद कोई positional पैरामीटर नहीं आ सकता।
खोलकर भेजना
स्टार कॉल करते समय भी इस्तेमाल हो सकते हैं, और तब वे उल्टा काम करते हैं:
def order(item, quantity):
return f"{quantity} x {item}"
values = ["pen", 3]
fields = {"item": "bag", "quantity": 2}
print(order(*values))
print(order(**fields))3 x pen
2 x bag*values लिस्ट को खोल देता है — order(values[0], values[1]) लिखने के बराबर। **fields डिक्शनरी को खोलता है, और उसकी key पैरामीटर के नामों से मेल खानी चाहिए।
परिभाषा में स्टार का मतलब इकट्ठा करना, कॉल में स्टार का मतलब खोलना — एक ही चिह्न, दो दिशाएँ।
एक पूरा उदाहरण
invoice.py:
# Arguments, in the order Python expects them
def invoice(customer, *items, currency="unit", discount=0.0, **extra):
total = sum(price for _, price in items)
after = total * (1 - discount)
lines = [f"Invoice for {customer}"]
for name, price in items:
lines.append(f" {name:<10} {price:>8.2f} {currency}")
lines.append(f" {'subtotal':<10} {total:>8.2f} {currency}")
if discount:
lines.append(f" {'discount':<10} {discount * 100:>7.0f}%")
lines.append(f" {'total':<10} {after:>8.2f} {currency}")
for key, value in extra.items():
lines.append(f" note {key}: {value}")
return "\n".join(lines)
print(invoice("rafi", ("pen", 15.0), ("bag", 850.0)))
print()
print(invoice("dia", ("ink", 120.0), discount=0.1, reference="A-77"))Invoice for rafi
pen 15.00 unit
bag 850.00 unit
subtotal 865.00 unit
total 865.00 unit
Invoice for dia
ink 120.00 unit
subtotal 120.00 unit
discount 10%
total 108.00 unit
note reference: A-77चार बातें ध्यान देने लायक हैं।
customer ज़रूरी है, बाकी सब वैकल्पिक। ग्राहक के बिना इनवॉइस का कोई मतलब नहीं, इसलिए वह सामान्य पैरामीटर है। प्रोडक्ट शून्य भी हो सकते हैं, इसलिए *items।
discount=0.0 सिर्फ़ डिफ़ॉल्ट नहीं है, उसने एक शर्त भी आसान कर दी। if discount: लाइन 8 नंबर अध्याय का truthiness वाला नियम इस्तेमाल कर रही है — 0.0 false है, इसलिए छूट न हो तो वह लाइन प्रिंट ही नहीं होती। और discount *items के बाद है, इसलिए keyword-only है — उसे discount=0.1 लिखकर ही भेजना पड़ता है।
`extra अनजानी जानकारी सँभाल रहा है।** reference="A-77" किसी पैरामीटर से मेल नहीं खाता, इसलिए वह extra` में जाता है और नोट के रूप में प्रिंट होता है। इस तरह फ़ंक्शन भविष्य के अनजाने फ़ील्ड भी ले सकता है।
for _, price in items — 14 नंबर अध्याय वाली अनपैकिंग, और अंडरस्कोर बता रहा है कि यहाँ यह नाम नहीं चाहिए। items टपल का एक टपल है, और हर अंदर वाला टपल अनपैक हो रहा है।
जब कुछ टूट जाए
SyntaxError: parameter without a default follows parameter with a default डिफ़ॉल्ट वाला पैरामीटर ज़रूरी वाले से पहले आ गया है। ज़रूरी वाले पहले, डिफ़ॉल्ट वाले बाद में।
TypeError: order() takes 2 positional arguments but 3 were given ज़्यादा आर्ग्युमेंट भेजे गए हैं। मैसेज दोनों बताता है — कितने लेता है और कितने दिए गए।
TypeError: order() got an unexpected keyword argument 'qty' नाम से भेजा गया, लेकिन उस नाम का कोई पैरामीटर नहीं है। स्पेलिंग मिलाकर देखें — या ध्यान दें कि फ़ंक्शन में **kwargs होता तो यह एरर नहीं आता, वह चुपचाप ले लेता।
TypeError: order() got multiple values for argument 'item' एक ही पैरामीटर एक बार position से और एक बार नाम से भेजा गया है — order("pen", item="bag")।
फ़ंक्शन ठीक चल रहा है, लेकिन जवाब उल्टा है आर्ग्युमेंट का क्रम उल्टा हो गया है, और फ़ंक्शन के अंदर का कोड दोनों तरह की वैल्यू के साथ चल सकता है, इसलिए कोई एरर नहीं आया। नाम से भेजें — यही वह चुपचाप होने वाली गलती है जो इस अध्याय की शुरुआत में दिखाई गई थी।
*` से भेज रहा हूँ, लेकिन संख्या मेल नहीं खा रही** order(*values) में values में जितनी चीज़ें हैं, फ़ंक्शन के उतने ही पैरामीटर चाहिए। print(len(values))` से देख लें।
चरण 4 / 6 — अनुमान
अपनी समझ की जाँच करें
quantity का एक डिफ़ॉल्ट है। दोनों लाइनें क्या प्रिंट करेंगी?
def order(item, quantity=1):
return f"{quantity} x {item}"
print(order("pen"))
print(order("pen", 5))- A1 x pen 5 x pen
- B1 x pen 1 x pen
- Cएक `TypeError`
- Dpen x 1 pen x 5
*things किस तरह की चीज़ इकट्ठा करता है?
def show(*things):
print(type(things))
print(len(things))
show(1, "two", 3.0)- A<class 'tuple'> 3
- B<class 'tuple'> 1
- C<class 'list'> 3
- D<class 'dict'> 3
डिफ़ॉल्ट वाला पैरामीटर पहले रखा गया है। क्या होगा?
def order(quantity=1, item):
return item- Aसामान्य तरह से चलता है
- Bचलता है, लेकिन `order("pen")` लिखने पर एरर आता है
- C`SyntaxError` — डिफ़ॉल्ट वाला पैरामीटर ज़रूरी वाले से पहले नहीं आ सकता
- D`TypeError`
उत्तर देने के लिए अकाउंट आवश्यक है
अपने उत्तर जाँचने के लिए साइन इन करें
प्रश्न ऊपर दिए गए हैं, और मन में उत्तर सोचना ही मुख्य कार्य है। सही उत्तर, व्याख्या और तीन-स्तरीय संकेत देखने के लिए साइन इन करें।
आपकी बारी
message.py नाम की एक फ़ाइल लिखें, जिसमें एक फ़ंक्शन हो:
def notify(recipient, *lines, subject="Notice", urgent=False, **details)फ़ंक्शन एक पढ़ने लायक मैसेज लौटाएगा (प्रिंट नहीं करेगा), जिसमें होंगे पाने वाला, विषय, हर लाइन अलग पंक्ति में, और details की हर जोड़ी नीचे नोट के रूप में। urgent True हो तो विषय से पहले कुछ जोड़ दें।
फिर फ़ंक्शन को चार तरह से कॉल करें:
- सिर्फ़ पाने वाले के साथ — बिना किसी लाइन के
- तीन लाइनों के साथ, बाकी सब डिफ़ॉल्ट पर
- नाम से
urgent=Trueऔर एकsubjectके साथ - एक लिस्ट और एक डिक्शनरी बनाकर, उन्हें
*और**से खोलकर
फिर जानबूझकर तीन बार तोड़ें, और हर बार देखें क्या होता है:
subject="Notice"पैरामीटर कोrecipientसे पहले ले जाएँ। फ़ाइल चलने से पहले हीSyntaxError: parameter without a default follows parameter with a defaultआएगा।notify("rafi", subjekt="hi")लिखकर गलत स्पेलिंग भेजें। कोई एरर नहीं आएगा:**detailsगलत नाम को भी चुपचाप ले लेता है, इसलिएsubjektएक नोट बन जाता है और विषय"Notice"ही रहता है। लौटा हुआ मैसेज प्रिंट करके अपनी आँखों से देखें।notify("rafi", recipient="dia")लिखें।TypeError: notify() got multiple values for argument 'recipient'आएगा।
दो गलतियाँ Python तुरंत पकड़ लेता है, एक नहीं पकड़ता — और वही एक सबसे खतरनाक है। **kwargs जो सुविधा देता है, उसकी कीमत यही है: स्पेलिंग की गलती अब कोई नहीं पकड़ता। मैसेज एक बार पहचान लें तो बाद में सेकंडों में पकड़ लेंगे, और यह भी पता रहेगा कि चुपचाप होने वाली गलती कहाँ छिपती है।
चरण 6 / 6
चुनौती — अध्याय की क्विज़
सरल से कठिन — दस प्रश्न, अंतिम वाले जानबूझकर चुनौतीपूर्ण बनाए गए हैं।
क्विज़ देने के लिए साइन इन करें