# ফাংশন আর্গুমেন্ট

মনে আছে আমরা আগের চ্যাপ্টারে ফাংশনকে একটি ছোট্ট মেশিন হিসেবে কল্পনা করেছিলাম। যেকোনো মেশিন বা যন্ত্র যখন বানানো হয় তখন তার কাজের জন্য যেমন কিছু যন্ত্রপাতির সেটআপ দরকার হয় তেমনি সেই মেশিনে ইনপুট হিসেবে কিছু কাঁচামাল দিতে হয় যেগুলো প্রক্রিয়াজাত করে মেশিন আমাদের চাহিদা মোতাবেক জিনিষ তৈরি করে দেয় বা এর থেকে আউটপুট পাওয়া যায়।\
ধরে নিচ্ছি আমাদের বানানো মেশিনটির এক পাশ দিয়ে ময়দা, চিনি, দুধ, ক্রিম এসব দিলে আরেক পাশ দিয়ে সুন্দর কেক তৈরি হয়ে বের হয়। তাহলে সেই ময়দা, চিনি, দুধ, ক্রিম এসব হচ্ছে সেই মেশিনের **আর্গুমেন্ট** আর কেক বানানোর জন্য মেশিনের মধ্যে বিভিন্ন যন্ত্রের যে সেটআপ আছে সেটাকে বলা যেতে পারে **ফাংশন বডি**। আর শেষে যে সুস্বাদু কেক পাওয়া যায় তাকে বলা যেতে পারে ফাংশনের **রিটার্ন ভ্যালু**। এখন এরকম একটি মেশিন তৈরি হয়ে গেলে এই মেশিনকে যতবার ইচ্ছা ব্যবহার করা যাবে এবং এর থেকে কেক পাওয়া যাবে। কিন্তু অবশ্যই প্রতিবার সঠিকভাবে কেক পেতে হলে এই মেশিনের আর্গুমেন্ট তথা কাঁচামাল গুলো দিতে হবে।

প্রোগ্রামিং -এও একই ভাবে একটি ফাংশনের কিছু আর্গুমেন্ট থাকতে পারে যেগুলো পক্ষান্তরে ফাংশন বডির মধ্যে ব্যবহৃত হয়ে চাহিদা মোতাবেক প্রসেসড হবে। এই আর্গুমেন্ট গুলো পাঠানোর দায়িত্ব হচ্ছে তার, যে এই ফাংশনকে কল করবে বা ব্যবহার করতে চাইবে। নিচের উদাহরণটি দেখি -

```python
def show_double(x):
    print(x*2)

show_double(2)
show_double(100)
```

আউটপুট,

```python
4
200
```

উপরে `show_double` ফাংশনের আর্গুমেন্ট একটি। আর তাই যখনই আমরা এই ফাংশনকে কল করেছি বা ব্যবহার করতে চেয়েছি তখনি সেই ফাংশনের আর্গুমেন্ট (মেশিনের ক্ষেত্রে ইনপুট) পাঠিয়ে দিয়েছি এভাবে `show_double(2)`। একবার কল করার সময় ইনপুট দিয়েছি `2` আবার আরেকবার কল করার সময় ইনপুট দিয়েছি `100` এবং আমাদের ফাংশনের কাজ হচ্ছে এর কাছে আসা যেকোনো আর্গুমেন্টকে দ্বিগুণ করে স্ক্রিনে প্রিন্ট করে। তাই দুইবারই আমাদের ফাংশন কাজটি সঠিক ভাবে করেছে।

> আর্গুমেন্টকে ফাংশনের দুটি প্রথম বন্ধনীর মধ্যে ডিফাইন করতে হয়।

একটি ফাংশন কিন্তু একাধিক আর্গুমেন্ট নিয়ে কাজ করতে পারে অর্থাৎ এর একাধিক আর্গুমেন্ট থাকতে পারে। এটাই তো যৌক্তিক, তাই না? কারণ, একটি ফাংশন তথা মেশিনকে জটিল জটিল জিনিষ বানাতে বা আউটপুট দিতে তাকে অনেক গুলো ইনপুট নিয়ে কাজ করতে হতেই পারে। নিচের উদাহরণটি দেখি -

```python
def make_sum(x, y):
    z = x + y
    print(z)

make_sum(5, 10)
make_sum(500, 500)
```

আউটপুট,

```python
15
1000
```

একটি বিষয় খেয়াল করুন, ফাংশনের আর্গুমেন্ট গুলোকে তার নিজের বডির মধ্যে একই নামের ভ্যারিয়েবল হিসেবে ব্যবহার করা যায়। যেমন উপরের উদাহরণে, `make_sum` ফাংশনের কাছে দুটো আর্গুমেন্ট এসেছে `x`, এবং `y` নামে এবং এই দুটি ভ্যালুকে সে নিজের বডির মধ্যে ব্যবহার করেছে যোগ করার জন্য এবং যোগফল জমা করেছে `z` নামের আরেকটি ভ্যারিয়েবলে।\
কিন্তু এই `x`, `y` বা `z` কে উক্ত ফাংশনের বাইরে থেকে অ্যাক্সেস করা যাবে না বা ব্যবহার করা যাবে না। যেমন -

```python
def make_sum(x, y):
    z = x + y
    print(z)

make_sum(5, 10)
print(z)
```

আউটপুট,

```python
15
...
NameError: name 'z' is not defined
```

উপরের উদাহরণে, `print(z)` স্টেটমেন্টটি এরর দেখাচ্ছে কারণ `z` ভ্যারিয়েবলের গণ্ডি বা স্কোপ ছিল শুধুমাত্র `make_sum` ফাংশনের মধ্যেই। তাই বাইরে থেকে একে অ্যাক্সেস করা যায় নি।

**মাল্টিপল প্যারামিটার হ্যান্ডলিং | আর্বিটরারি আর্গুমেন্ট লিস্ট**

মনে করুন, আপনি `make_sum` ফাংশনটিতে অনেকগুলো প্যারামিটার পাঠাতে চাচ্ছেন যেমন, 10, 20, 30 ... ইত্যাদি। যদি আপনি `make_sum (a, b)` হিসেবে ডিক্লেয়ার করেন তাহলে দুইটার বেশি প্যারামিটার পাঠাতে পারবেন না। সেক্ষেত্রে কোড হবে এইরকম,

```python
def make_sum(*args):
    sum = 0
    for num in args: # Here, args is like a Tuple which is (10, 20, 30, 40)
        sum += num
    return sum

print(make_sum(10, 20, 30, 40))
```

**আউটপুট**

```
100
```

## পাইথনে `*` এর অর্থ

`*` এর আর্গুমেন্টে ভ্যালু `Tuple` হিসেবে প্যাকড থাকে। এর মানে `*` দিয়ে প্যারামিটার ডিক্লেয়ার করলে আমরা যেকোন সংখ্যক পজিশনাল আর্গুমেন্ট পাস করতে পারি। যেমন করলাম `make_sum` এর ক্ষেত্রে। শুরুতে `make_sum` মাত্র দুইটা আর্গুমেন্ট নিলেও পরবর্তীতে আমরা প্যারামিটারে `*` বসিয়ে দিলাম তখন সে অনেকগুলো আর্গুমেন্ট পাস করতে পারছে।

## পাইথনে `**` এর অর্থ

আমরা চাইলে ফাংশনের প্যারামিটারে ডাবল অ্যাস্টেরিস্কস বসিয়েও ডিক্লেয়ার করতে পারি। ডাবল স্টারের মানে হল যেকোন সংখ্যক `named parameter` থাকতে পারে। এই মানগুলো ডিকশনারি হিসেবে প্যাকড থাকে। নিচের উদাহরণটি লক্ষ্য করা যাক,

```python
def print_dict(*args):
    print (args)


print_dict(a=1, b=2)
```

আউটপুট,

```python
TypeError                                 Traceback (most recent call last)
<ipython-input-2-9970453fce76> in <module>()
----> 1 print_dict(a=1, b=2)

TypeError: print_dict() got an unexpected keyword argument 'a'
```

সিঙ্গেল অ্যাস্টেরিস্কস ব্যবহার করলে আমরা নেমড আর্গুমেন্ট পাস করতে পারব না। তাই আমাদের এসব ক্ষেত্রে ডাবল অ্যাস্টেরিস্কস ব্যবহার করতে হবে, যেমন

```python
def print_dict(**kwargs):
    print(kwargs)


print_dict(a=1, b=2, c=3)
```

আউটপুট,

```python
{'a': 1, 'c': 3, 'b': 2}
```

আমরা যদি কোডটা আরেকটু গুছিয়ে লেখি,

```python
def print_dict(**kwargs):
    for args in kwargs:
        print("{0} : {1}".format(args, kwargs[args]))


print_dict(a=1, b=2, c=3)
```

আউটপুট,

```python
a : 1
c : 3
b : 2
```

চাইলে আমরা মিক্সড ভ্যারিয়েডিক আর্গুমেন্ট পাঠাতে পারি। মানে একই ফাংশনে তিন ধরণের আর্গুমেন্ট, তবে খেয়াল রাখতে হবে প্যারামিটারগুলো এমন ভাবে ডিফাইন করা হয় যেন প্রথমে সাধারণ প্যারামিটার তারপরে সিঙ্গেল অ্যাস্টেরিস্কের প্যারামিটার এবং অবশেষে ডাবল অ্যাস্টেরিস্কস এর প্যারামিটার থাকে। মানে আমাদের অবশ্যই ক্রম মানতে হবে এইক্ষেত্রে।

```python
def print_all(a, *args, **kwargs):
    print(a)
    print(args)
    print(kwargs)


print_all(10, 20, 30, 50, b=5, c=10)
```

আউটপুট,

```python
10
(20, 30, 50)
{'c': 10, 'b': 5}
```

**প্যারামিটার ও আর্গুমেন্ট**

যখন একটি ফাংশনকে ডিফাইন করা হয় তখন এর ভ্যারিয়েবল গুলোকে প্যারামিটার বলা হয়। আর যখন একটি ফাংশনকে কল করা হয় তখন সেই ফাংশনের প্যারামিটার হিসেবে যে ভ্যালু পাঠানো হয় তাকে আর্গুমেন্ট বলা হয়।

> সংকলন - [নুহিল মেহেদী](https://nuhil.net)
>
> পরিমার্জন - [মানস](http://mandal.manash.me)
