# Python-এর ফাংশন: def, return, আর্গুমেন্ট, *args ও **kwargs, lambda ও টাইপ অ্যানোটেশন

URL: https://skillaria.top/bn/guides/python/functions-python

Python-এর ফাংশন হলো একটি নামযুক্ত কোড-ব্লক, যা def দিয়ে ঘোষণা করা হয়, নাম ও বন্ধনী দিয়ে ডাকা হয় এবং return দিয়ে ফল ফেরত দেয়; return না থাকলে ফল হয় None। আর্গুমেন্ট ক্রমে বা নাম ধরে পাঠানো যায়, *args বাড়তি পজিশনাল মানগুলো tuple-এ এবং **kwargs বাড়তি নামযুক্ত মানগুলো dictionary-তে জমায়, আর lambda একটি এক্সপ্রেশনের ছোট নামহীন ফাংশন বানায়।

Python-এর ফাংশন হলো নামযুক্ত একটি কোড-ব্লক, যাকে যতবার খুশি ভিন্ন ইনপুট দিয়ে ডাকা যায়। ঘোষণা হয় `def` শব্দ দিয়ে, মান নেয় প্যারামিটারের মাধ্যমে, আর ফল দেয় `return` দিয়ে। লম্বা স্ক্রিপ্টকে বোধগম্য, যাচাইযোগ্য ধাপে ভাঙার প্রধান উপায় এই ফাংশন।

## ফাংশন ঘোষণা: def

ফাংশন ঘোষণা শুরু হয় `def` দিয়ে, তারপর নাম, বন্ধনীতে প্যারামিটার এবং কোলন। শরীর লেখা হয় ৪ স্পেস ইন্ডেন্টে — Python-এ ইন্ডেন্টেশন সাজসজ্জা নয়, সিনট্যাক্সের অংশ।

```
def greet(name):
    """ব্যবহারকারীর জন্য একটি স্বাগত বার্তা ফেরত দেয়।"""
    return f"স্বাগতম, {name}!"

print(greet("Anika"))   # স্বাগতম, Anika!
```

হেডারের পরে তিন উদ্ধৃতি চিহ্নের স্ট্রিং হলো docstring — ফাংশনের বর্ণনা। `help(greet)` এবং এডিটরের ইঙ্গিতে এটি দেখায়। ফাংশনের নাম PEP 8 অনুযায়ী snake_case-এ লেখা হয়: `add_expense`, `get_rate`।

`def greet(name):` লাইনকে বলা হয় ফাংশনের সিগনেচার: নাম ও প্যারামিটারের তালিকা (এবং অ্যানোটেশন থাকলে সেগুলোও)। সিগনেচার দেখে পাঠক শরীর না পড়েই ফাংশন কীভাবে ডাকতে হবে বুঝে যায়।

## 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` দেয়, এবং সমস্যাটি ধরা পড়ে এক লাইন পরে। একাধিক মান ফেরত দেওয়াও সম্ভব — আসলে সেটি একটি tuple, যা সহজেই আনপ্যাক করা যায়:

```
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  limit    # গ্লোবাল পড়া — চলে

def raise_limit():
    global limit
    limit = 2000             # global ছাড়া একটি লোকাল ভেরিয়েবল তৈরি হতো
```

বাস্তবে `global` কম ব্যবহার করা ভালো: বাইরের অবস্থা বদলায় এমন ফাংশন পরীক্ষা করা কঠিন। মান আর্গুমেন্ট হিসেবে নেওয়া এবং ফল ফেরত দেওয়া নিরাপদ।

## ভেতরের ফাংশন ও closure

একটি ফাংশনের ভেতরে আরেকটি ফাংশন ঘোষণা করা যায়। ভেতরের ফাংশন বাইরের ফাংশনের ভেরিয়েবল দেখে, এমনকি বাইরের ফাংশন শেষ হয়ে যাওয়ার পরেও — একে বলে closure। এমন ভেরিয়েবল বদলাতে লাগে `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
```

Closure-এর উপরেই দাঁড়িয়ে আছে decorator ও ফাংশন-ফ্যাক্টরি।

## ফাংশনও একটি অবজেক্ট

Python-এ ফাংশন একটি সাধারণ অবজেক্ট: তাকে ভেরিয়েবলে রাখা যায়, অন্য ফাংশনে পাঠানো যায়, ফাংশন থেকে ফেরত দেওয়া যায়, dictionary-তে জমানো যায়। তার অ্যাট্রিবিউট আছে: `__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 = [("Coffee", 250), ("Taxi", 600), ("Lunch", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses)  # [('Taxi', 600), ('Lunch', 450), ('Coffee', 250)]

print(list(filter(lambda n: n % 2 == 0, range(10))))  # [0, 2, 4, 6, 8]
```

Lambda ভালো মানায় `sorted`, `max`, `map`, `filter`-এর মতো ফাংশনে ছোট আর্গুমেন্ট হিসেবে। তবে lambda-কে ভেরিয়েবলে নাম দিয়ে বসানো, যেমন উপরের `square`, PEP 8 অনুমোদন করে না: নাম দরকার হলে `def` লিখুন — তখন ট্রেসব্যাকে নরমাল `__name__` থাকে এবং docstring-এর জায়গা থাকে।

|  | def | lambda |
| --- | --- | --- |

| নাম | আছে | নেই (`<lambda>`) |

| শরীর | যতগুলো খুশি নির্দেশ | একটি এক্সপ্রেশন |

| return | স্পষ্ট | অন্তর্নিহিত |

| Docstring, অ্যানোটেশন | আছে | নেই |

| কোথায় উপযোগী | সর্বত্র | ছোট key বা callback-এ |

## টাইপ অ্যানোটেশন: ইঙ্গিত, যাচাই নয়

অ্যানোটেশন প্যারামিটার ও ফলের প্রত্যাশিত ধরন বর্ণনা করে। Python চালানোর সময় এগুলো যাচাই করে না: `x: int` লেখা ফাংশন একটি স্ট্রিংও নীরবে নেবে। যাচাই করে বাইরের টুল — mypy, pyright, ty — এবং এডিটর, যা চালানোর আগেই ভুল দেখায়।

```
def add_expense(title: str, amount: float, category: str = "other") -> 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 দিয়ে যাচাই করা হয়।

## FAQ

**প্যারামিটার আর আর্গুমেন্টের পার্থক্য কী?**

প্যারামিটার হলো ফাংশন ঘোষণার নাম, আর আর্গুমেন্ট হলো ডাকার সময় দেওয়া নির্দিষ্ট মান। def greet(name)-এ name প্যারামিটার, আর greet("Anika")-য় "Anika" স্ট্রিংটি আর্গুমেন্ট।

**ঘোষণার আগে কি ফাংশন ডাকা যায়?**

মডিউল স্তরে না: def লাইনটি আগে চলতে হবে, নইলে NameError। তবে অন্য একটি ফাংশনের ভেতর থেকে পরে ঘোষিত ফাংশন ডাকা যায়, যদি সেই ডাকটি ঘোষণার পরে ঘটে।

**একটি ফাংশন কতগুলো মান ফেরত দিতে পারে?**

আনুষ্ঠানিকভাবে একটি, তবে সেই একটি মান tuple-ও হতে পারে: return a, b দুটি উপাদানের একটি tuple ফেরত দেয়, যেটি x, y = f() লিখে আনপ্যাক করা যায়।

**টাইপ অ্যানোটেশন লেখা কি বাধ্যতামূলক?**

না, অ্যানোটেশন ছাড়াও Python চলে। তবে অন্যদের পড়ার কোডে অ্যানোটেশন রক্ষণাবেক্ষণ সহজ করে এবং mypy বা pyright-কে চালানোর আগেই ভুল ধরতে দেয়।

**lambda-র ভেতরে if লেখা যায় কি?**

শুধু শর্তসূচক এক্সপ্রেশন যায়: lambda x: "জোড়" if x % 2 == 0 else "বিজোড়"। lambda-র ভেতরে নির্দেশ, লুপ বা অ্যাসাইনমেন্ট চলে না।


---
Source: Skillaria.Top (https://skillaria.top)
