Table of Contents
چرا توابع مهم هستند؟
در برنامهنویسی، توابع مثل «بلوکهای سازنده» هستند که از آنها برای ساختن برنامههای بزرگتر استفاده میکنیم. چند دلیل مهم بودن توابع:
- تقسیم مسئلهٔ بزرگ به قسمتهای کوچک
بهجای نوشتن یک کد طولانی و شلوغ، مسئله را به چند کار کوچکتر تبدیل میکنیم و برای هر کار یک تابع مینویسیم. این کار فهمیدن و عوضکردن کد را آسان میکند. - استفادهٔ مجدد از کد (reusability)
اگر لازم باشد یک کار را چند بار انجام دهیم (مثلاً محاسبهٔ مساحت دایره)، لازم نیست هر بار فرمول را بنویسیم؛ یک بار تابع مینویسیم و هر جا لازم بود آن را صدا میزنیم (call میکنیم). - کاهش خطا و سادهتر شدن اشکالزدایی
وقتی هر بخشِ برنامه در یک تابع جداگانه است، اگر خطایی رخ دهد، راحتتر میفهمیم در کدام بخش مشکل داریم. همچنین عوضکردن رفتار یک بخش، فقط در همان تابع انجام میشود. - خواناتر شدن کد
اسم تابع میتواند توضیحدهندهٔ کار آن باشد. مثلاًprint_invoice()از ده خط کد داخلش فهمپذیرتر است. - امکان همکاری گروهی
در پروژههای بزرگ، هر نفر روی چند تابع/ماژول کار میکند. اگر کد به توابع مرتب تقسیم شده باشد، کار گروهی بسیار راحتتر میشود.
یک مثال ساده از استفادهٔ تابع بهجای تکرار کد:
# بدون تابع (تکرار کد)
r1 = 5
area1 = 3.14 * r1 * r1
r2 = 10
area2 = 3.14 * r2 * r2
print(area1, area2)
# با تابع
def circle_area(r):
return 3.14 * r * r
print(circle_area(5), circle_area(10))
در نسخهٔ دوم، اگر فرمول را عوض کنیم، فقط داخل تابع circle_area را تغییر میدهیم.
تعریف توابع
در پایتون، توابع با کلمهٔ کلیدی def تعریف میشوند. ساختار کلی:
$$
\text{def} \ \text{name}(\text{parameters}): \\
\quad \text{body}
$$
بهصورت کد:
def name(parameters):
# بدنهٔ تابع
# دستورها
# ...نکتهها:
- بعد از
def، اسم تابع میآید (مثل نام متغیر، نباید با عدد شروع شود و نباید فاصله داشته باشد). - داخل پرانتز میتوانیم صفر یا بیش از یک «پارامتر» داشته باشیم.
- در پایان خط
:میآید. - کدِ بدنهٔ تابع باید تورفتگی (indent) داشته باشد (مثلاً ۴ فاصلهٔ خالی یا یک تب).
مثال: یک تابع ساده بدون ورودی و بدون خروجی
def say_hello():
print("سلام!")
print("به دنیای پایتون خوش آمدی.")
# فراخوانی تابع
say_hello()اینجا:
say_helloنام تابع است.- هیچ پارامتری ندارد.
- داخل بدنهاش فقط چاپ (
print) انجام میشود. - برای اجرای کد تابع، باید آن را فراخوانی کنیم:
say_hello().
محل تعریف تابع
تابع را معمولاً در ابتدای فایل (بالای کد اصلی) تعریف میکنیم، بعد در قسمتهای مختلف برنامه آن را فراخوانی میکنیم. اگر قبل از تعریف تابع، آن را فراخوانی کنید، پایتون خطا میدهد، چون هنوز آن را نمیشناسد.
پارامترها و آرگومانها
وقتی میخواهیم تابع روی دادههای مختلف کار کند، از پارامترها و آرگومانها استفاده میکنیم.
- پارامتر (parameter): نام متغیرهایی که در تعریف تابع داخل پرانتز مینویسیم.
- آرگومان (argument): مقادیری که هنگام فراخوانی تابع به آن میدهیم.
مثال ساده با یک پارامتر
def greet(name):
print("سلام", name)
greet("علی")
greet("زهرا")nameیک پارامتر است."علی"و"زهرا"آرگومان هستند.
چند پارامتر
def add(a, b):
print("جمع =", a + b)
add(3, 5) # a = 3, b = 5
add(10, 20) # a = 10, b = 20پارامترها از چپ به راست، بر اساس ترتیب آرگومانهایی که در فراخوانی مینویسیم مقدار میگیرند.
پارامترهای پیشفرض (default parameters)
میتوانیم برای بعضی پارامترها مقدار پیشفرض تعریف کنیم. اگر آرگومانِ مربوطه را ندهیم، پایتون آن مقدار پیشفرض را استفاده میکند.
def power(base, exponent=2):
print(base ** exponent)
power(3) # 3 به توان 2 -> 9
power(2, 3) # 2 به توان 3 -> 8اینجا:
- اگر فقط
power(3)بنویسیم،exponentبهطور خودکار ۲ در نظر گرفته میشود. - اگر
power(2, 3)بنویسیم، مقدار پیشفرض نادیده گرفته میشود وexponent = 3میشود.
نکته: در تعریف تابع، پارامترهای بدون مقدار پیشفرض باید قبل از پارامترهای دارای مقدار پیشفرض بیایند:
def f(a, b=10): # درست
...
# def f(a=10, b): # نادرست، خطا میدهد
# ...آرگومانهای نامگذاریشده (keyword arguments)
هنگام فراخوانی تابع، میتوانیم نام پارامتر را هم بنویسیم تا شفافتر شود:
def introduce(name, age):
print("نام:", name)
print("سن:", age)
introduce(name="علی", age=25)
introduce(age=30, name="مهدی") # ترتیب مهم نیستاستفاده از آرگومان نامگذاریشده کد را خواناتر و در بعضی حالتها امنتر میکند (بهخصوص وقتی تعداد پارامترها زیاد است).
مقادیر بازگشتی
بعضی تابعها فقط کاری انجام میدهند (مثل چاپ روی صفحه) و چیزی «برنمیگردانند». بعضی دیگر نتیجهٔ محاسبه را برمیگردانند تا بتوانیم بعداً از آن استفاده کنیم. برای برگرداندن مقدار از کلمهٔ کلیدی return استفاده میکنیم.
def add(a, b):
result = a + b
return result
x = add(3, 5) # نتیجهٔ تابع در x ذخیره میشود
print(x)نکتهها:
- بعد از اجرای
return، تابع فوراً پایان مییابد و بقیهٔ خطوطِ بدنهٔ تابع اجرا نمیشوند. - میتوانیم مستقیماً مقدار را برگردانیم:
def multiply(a, b):
return a * b
print(multiply(4, 6))تابع بدون `return`
اگر در تابع از return استفاده نکنیم (یا return بدون مقدار بنویسیم)، مقدار بازگشتی تابع None است.
def show_message():
print("پیام مهم!")
result = show_message()
print(result) # چاپ میکند: Noneاینجا:
- تابع کار خودش (چاپ پیام) را انجام میدهد.
- ولی مقدار خاصی برنمیگرداند، پس
resultمقدارNoneمیگیرد.
بازگشت چند مقدار
در پایتون میتوانیم چند مقدار را با هم برگردانیم. در واقع یک تاپل برگردانده میشود:
def get_min_max(numbers):
smallest = min(numbers)
largest = max(numbers)
return smallest, largest
mn, mx = get_min_max([3, 8, 1, 9, 4])
print("کمترین:", mn)
print("بیشترین:", mx)
پایتون بهطور خودکار مقادیر برگرداندهشده را «باز میکند» (unpack) و در متغیرهای mn و mx میریزد.
محدودهٔ متغیرها (محلی در برابر سراسری)
وقتی با توابع کار میکنیم، مهم است بدانیم هر متغیر کجا قابل استفاده است؛ به این مفهوم میگویند محدودهٔ متغیر (scope).
متغیر محلی (local)
متغیری که داخل تابع تعریف میشود، فقط داخل همان تابع قابل استفاده است.
def say_name():
name = "علی" # متغیر محلی
print(name)
say_name()
# print(name) # خطا: name تعریف نشده است
اینجا name فقط داخل say_name وجود دارد. بیرون تابع، پایتون این نام را نمیشناسد.
متغیر سراسری (global)
متغیری که خارج از همهٔ توابع تعریف شود، در سراسر فایل (در تابعها هم) در دسترس است (با بعضی نکات مهم).
message = "سلام سراسری!" # متغیر سراسری
def show():
print(message) # میتواند به message دسترسی داشته باشد
show()
print(message)تابع میتواند مقدار متغیر سراسری را بخواند. اما اگر داخل تابع به آن مجدداً مقداردهی کنیم، اتفاق دیگری میافتد.
سایهانداختن (shadowing)
اگر داخل تابع متغیری با همان نام متغیر سراسری تعریف کنیم، متغیر محلی، متغیر سراسری را «پوشش» میدهد:
x = 10 # سراسری
def test():
x = 5 # محلی، سراسری را نمیسازد و فقط در این تابع معتبر است
print("داخل تابع:", x)
test()
print("بیرون تابع:", x)خروجی:
داخل تابع: 5
بیرون تابع: 10تغییر متغیر سراسری داخل تابع
اگر واقعاً بخواهیم داخل تابع، متغیر سراسری را عوض کنیم، باید از کلمهٔ global استفاده کنیم (این کار معمولاً در برنامههای بزرگ توصیه نمیشود، مگر با احتیاط):
count = 0
def increase():
global count # اعلام میکنیم که منظورمان متغیر سراسری است
count = count + 1
increase()
increase()
print(count) # 2
اگر global count را ننویسیم و داخل تابع به count مقدار بدهیم، در واقع یک count محلی جدید ساخته میشود که جدا از متغیر سراسری است.
توصیهٔ مهم
- تا حد امکان، از دستکاری متغیرهای سراسری در داخل تابعها خودداری کنید.
- بهتر است دادهها را «بهعنوان پارامتر» به تابع بدهید و نتیجه را «با
return» تحویل بگیرید. این کار کد را تمیزتر و قابلپیشبینیتر میکند.
استفادهٔ مجدد از کد
یکی از مهمترین دلایل استفاده از توابع، قابلیت استفادهٔ مجدد (reusability) است؛ یعنی:
- یک بار منطق مورد نیاز را مینویسیم،
- سپس هر چند بار لازم باشد، از آن استفاده میکنیم،
- بدون کپیکردن کد.
مثال: اعتبارسنجی سن
فرض کنید چند جای برنامه باید بررسی کنیم سن واردشده معتبر است یا نه (مثلاً بین ۰ و ۱۲۰).
بدون تابع:
age = int(input("سن را وارد کنید: "))
if age < 0 or age > 120:
print("سن نامعتبر است")
else:
print("سن معتبر است")
# جای دیگر برنامه
age2 = int(input("سن دوم را وارد کنید: "))
if age2 < 0 or age2 > 120:
print("سن نامعتبر است")
else:
print("سن معتبر است")با تابع:
def is_valid_age(age):
return 0 <= age <= 120
age = int(input("سن را وارد کنید: "))
if is_valid_age(age):
print("سن معتبر است")
else:
print("سن نامعتبر است")
age2 = int(input("سن دوم را وارد کنید: "))
if is_valid_age(age2):
print("سن دوم معتبر است")
else:
print("سن دوم نامعتبر است")اینجا:
- منطق اعتبارسنجی فقط در یک نقطه تعریف شده (
is_valid_age). - اگر بعداً قاعدهٔ اعتبارسنجی عوض شود (مثلاً حداکثر ۹۹ شود)، فقط باید تابع را تغییر دهیم، نه همهٔ جاهایی که استفاده شدهاند.
ساختن کتابخانهٔ توابع شخصی
وقتی توابع مفید زیادی نوشتید (مثلاً توابعی برای محاسبات خاص، کار با رشتهها، پردازش فایلها)، میتوانید:
- آنها را در یک فایل جدا (مثلاً
utils.py) قرار دهید، - و در پروژههای مختلف آن فایل را وارد کنید (این کار در فصل کتابخانهها توضیح داده میشود).
این یعنی کد شما واقعاً «قابل استفادهٔ مجدد» میشود، نه فقط در یک فایل، بلکه در چند پروژهٔ مختلف.
نکتههای طراحی تابعهای قابلاستفادهٔ مجدد
- اسم تابع را طوری انتخاب کنید که معنایش را روشن بگوید (
calculate_discountبهتر ازcdاست). - سعی کنید تابع فقط یک کار مشخص انجام دهد (اصل «یک مسئولیت»).
- از پارامترها برای تغییردادن رفتار تابع استفاده کنید، نه از متغیرهای سراسری.
- تابعهایتان را با ورودیهای مختلف امتحان کنید تا مطمئن شوید درست کار میکنند.
با یادگرفتن و تمرین توابع، قدم بزرگی از «نوشتن چند خط کد ساده» بهسمت «طراحی برنامههای تمیز، قابلفهم و قابلتوسعه» برمیدارید.