ফাংশনের আর্গুমেন্ট — নাম, ডিফল্ট আর তারকা
নাম ধরে আর্গুমেন্ট পাঠানো, ডিফল্ট মান ও তাদের ক্রমের নিয়ম, *args আর **kwargs, আর কল করার সময় লিস্ট বা ডিকশনারি খুলে পাঠানো।
- 1সমস্যা
- 2বোঝা
- 3উদাহরণ
- 4অনুমান
- 5নিজে করা
- 6কঠিন করা
যে সমস্যাটা আমরা সমাধান করছি
গত অধ্যায়ের ফাংশনগুলো একটিই আকারে ডাকা যেত: যতগুলো প্যারামিটার, ঠিক ততগুলো আর্গুমেন্ট, ঠিক সেই ক্রমে।
বাস্তবে সেটা যথেষ্ট নয়। একটি অর্ডারের ফাংশন ভাবুন:
def order(item, quantity):
return f"{quantity} x {item}"
print(order("pen", 3))
print(order(3, "pen"))3 x pen
pen x 3দ্বিতীয় লাইনটি কোনো ত্রুটি দেয়নি — শুধু অর্থহীন উত্তর দিয়েছে। প্যারামিটার দুটি একই ধরনের হলে পাইথনের বলার কোনো উপায়ই নেই যে ক্রম উল্টে গেছে।
আর প্রায় সব অর্ডারই একটিমাত্র জিনিসের হলে? প্রতিবার 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") লিখলে পাইথন কীভাবে বুঝবে "pen" কোনটির জন্য? আবশ্যকগুলো আগে থাকলে সেই দ্ব্যর্থতা থাকে না।
যেকোনো সংখ্যক আর্গুমেন্ট — *args
def total(*prices):
return sum(prices)
print(total(15, 60))
print(total(15, 60, 850))
print(total())75
925
0*prices-এর তারকাটি বলছে «বাকি সব পজিশনাল আর্গুমেন্ট এখানে জড়ো করো»। কয়টি আসবে তা আগে থেকে ঠিক করার দরকার নেই, আর একটিও না এলে সমস্যা নেই।
জড়ো করা জিনিসটি আসলে কী?
def show(*things):
print(type(things))
print(things)
show(1, "two")<class 'tuple'>
(1, 'two')একটি টাপল — চতুর্দশ অধ্যায়ের সেই টাপল। যুক্তিটা সুন্দর: আর্গুমেন্টের সংখ্যা কলের সময়ই ঠিক হয়ে যায় আর তারপর বদলানোর কথা নয়, তাই অপরিবর্তনীয় পাত্রই মানানসই।
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দুটি তারকা নাম-ধরে-পাঠানো আর্গুমেন্টগুলো জড়ো করে, আর ফলটি একটি ডিকশনারি — নাম আর মানের জোড়া, ঠিক যা হওয়ার কথা।
একটি তারকা মানে টাপল, দুটি মানে ডিকশনারি। এই জোড়াটা মনে রাখলে বাকিটা অনুমান করা যায়।
ক্রমটা নির্দিষ্ট
একসাথে লিখলে ক্রম হবে: *সাধারণ, তারপর `args, তারপর ডিফল্টওয়ালা, তারপর kwargs`।
def f(a, *args, b=1, **kwargs):প্রথম দেখায় জটিল লাগে, কিন্তু কারণটা সরল: পাইথনকে জানতে হবে কোন আর্গুমেন্টটি কোথায় যাবে, আর *args যেহেতু «বাকি সব» নেয়, তার পরে আর কোনো পজিশনাল আর্গুমেন্ট থাকতে পারে না।
খুলে পাঠানো
তারকাগুলো কল করার সময়ও ব্যবহার করা যায়, আর তখন তারা উল্টো কাজ করে:
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 ডিকশনারিটিকে খোলে, আর কী-গুলো প্যারামিটারের নামের সাথে মেলাতে হয়।
সংজ্ঞায় তারকা মানে জড়ো করা, কলে তারকা মানে খোলা — একই চিহ্ন, দুই দিক।
একটা সম্পূর্ণ উদাহরণ
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: লাইনটি অষ্টম অধ্যায়ের সত্যতার নিয়ম ব্যবহার করছে — 0.0 মিথ্যা, তাই ছাড় না থাকলে সেই সারিটি ছাপাই হয় না।
`extra অজানা তথ্য সামলাচ্ছে।** reference="A-77" কোনো প্যারামিটারের সাথে মেলে না, তাই সে extra`-তে গিয়ে পড়ে আর নোট হিসেবে ছাপা হয়। এভাবে ফাংশনটি ভবিষ্যতের অজানা ক্ষেত্রগুলোও নিতে পারে।
for _, price in items — চতুর্দশ অধ্যায়ের আনপ্যাকিং, আর আন্ডারস্কোরটি বলছে নামটা এখানে লাগবে না। 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' একই প্যারামিটার একবার অবস্থান ধরে আর একবার নাম ধরে পাঠানো হয়েছে — order("pen", item="bag")।
ফাংশনটি ঠিক চলছে, কিন্তু উত্তর উল্টো আর্গুমেন্টের ক্রম উল্টে গেছে, আর ধরন একই হওয়ায় পাইথন ধরতে পারেনি। নাম ধরে পাঠান — এটিই সেই নীরব ভুল যা এই অধ্যায়ের শুরুতে দেখানো হয়েছিল।
*` দিয়ে পাঠাচ্ছি, কিন্তু সংখ্যা মিলছে না** order(*values)-এ values-এ যতগুলো জিনিস, ফাংশনের ততগুলো প্যারামিটার লাগবে। print(len(values))` দিয়ে দেখে নিন।
ধাপ ৪ / ৬ — অনুমান
যাচাই করুন
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
- Cpen x 1 pen x 5
- Dএকটি `TypeError`
*things কী ধরনের জিনিস জড়ো করে?
def show(*things):
print(type(things))
print(len(things))
show(1, "two", 3.0)- A<class 'tuple'> 3
- B<class 'list'> 3
- C<class 'dict'> 3
- D<class 'tuple'> 1
ডিফল্টওয়ালা প্যারামিটারটি আগে বসানো হয়েছে। কী হবে?
def order(quantity=1, item):
return item- A`SyntaxError` — ডিফল্টওয়ালা প্যারামিটার আবশ্যকটির আগে বসতে পারে না
- Bকাজ করে, কিন্তু `order("pen")` লিখলে ত্রুটি হয়
- Cকাজ করে স্বাভাবিকভাবেই
- D`TypeError`
উত্তর দিতে অ্যাকাউন্ট লাগবে
উত্তর মিলিয়ে দেখতে সাইন ইন করুন
প্রশ্নগুলো উপরে আছে, আর মাথায় মাথায় উত্তর ভেবে নেওয়াই আসল কাজ। সঠিক উত্তর, ব্যাখ্যা আর তিন ধাপের ইঙ্গিত দেখতে সাইন ইন করুন।
নিজে করুন
message.py নামে একটি ফাইল লিখুন, যাতে একটি ফাংশন থাকবে:
def notify(recipient, *lines, subject="Notice", urgent=False, **details)ফাংশনটি একটি পড়ার মতো বার্তা ফেরত দেবে (ছাপবে না), যেখানে থাকবে প্রাপক, বিষয়, প্রতিটি লাইন আলাদা সারিতে, আর details-এর প্রতিটি জোড়া নিচে নোট হিসেবে। urgent সত্য হলে বিষয়ের আগে কিছু একটা যোগ করুন।
তারপর ফাংশনটিকে চারভাবে ডাকুন:
- শুধু প্রাপক দিয়ে — কোনো লাইন ছাড়াই
- তিনটি লাইন দিয়ে, বাকি সব ডিফল্টে
- নাম ধরে
urgent=Trueআর একটিsubjectদিয়ে - একটি লিস্ট আর একটি ডিকশনারি বানিয়ে,
*আর**দিয়ে খুলে পাঠিয়ে
তারপর ইচ্ছে করে তিনবার ভাঙুন, আর প্রতিটি বার্তা পড়ুন:
- ডিফল্টওয়ালা প্যারামিটারটিকে
recipient-এর আগে সরিয়ে দিন subjekt="hi"বানান ভুল করে পাঠানnotify("rafi", recipient="dia")লিখুন
তিনটিই আলাদা SyntaxError বা TypeError দেবে, আর তিনটিই বাস্তবে নিয়মিত ঘটে। বার্তাগুলো একবার চিনে রাখলে পরে সেকেন্ডে ধরতে পারবেন।
ধাপ ৬ / ৬
কঠিন করা — অধ্যায়ের কুইজ
সহজ থেকে কঠিন — দশটি প্রশ্ন, শেষেরগুলো ইচ্ছে করেই কঠিন।
সাইন ইন করে কুইজ দিন