تابع
کد را به تکههای اسمدار و قابلاستفادهی دوباره تقسیم کن: def، پارامتر و آرگومان، return و فرقش با print، مقدار پیشفرض و آرگومان با اسم، برگرداندن چند مقدار، docstring، دامنهی متغیرها، lambda برای sorted، و تلهی لیست بهعنوان مقدار پیشفرض.
- حدود ۲۰ دقیقه مطالعه
- ۱۰ مثال اجراشده
- ۳ تمرین با جواب
- مقدماتی تا متوسط
- بهروزرسانی: ۲۹ مهر ۱۴۰۵
توی این درس یاد میگیری تکهای از کد را یک بار با def بنویسی، به آن اسم بدهی و هر بار لازم شد صدایش بزنی. ورودی تابع را با پارامتر میدهی و نتیجه را با return پس میگیری (و میفهمی چرا return با print فرق دارد). مقدار پیشفرض و آرگومان با اسم را یاد میگیری، چند مقدار را با هم برمیگردانی، با docstring تابعت را توضیح میدهی، و میبینی متغیرهای داخل تابع چرا بیرونش دیده نمیشوند. lambda را هم برای مرتبسازی میشناسی.
مسئله: کپی کردن کد
Section titled “مسئله: کپی کردن کد”فرض کن در سه جای برنامه باید قیمت یک سبد خرید را حساب کنی. سه بار همان پنج خط را کپی میکنی. فردا قیمتها مالیات میگیرند و باید فرمول عوض شود: حالا سه جا را باید درست کنی، و احتمالاً یکی را فراموش میکنی. تابع این مشکل را حل میکند: یک بار بنویس، هر جا لازم شد صدا بزن، و اگر عوض شد، فقط یک جا را عوض کن.
تشبیه: دستگاه آبمیوهگیری
Section titled “تشبیه: دستگاه آبمیوهگیری”تابع مثل دستگاه آبمیوهگیری است: از یک طرف ورودی (پرتقال) میدهی، دستگاه کارش را میکند و از طرف دیگر خروجی (آب پرتقال) میدهد. لازم نیست هر بار بدانی داخلش چه میگذرد؛ فقط باید بدانی چه بدهی و چه پس بگیری. پارامتر دهانهی ورودی است و return شیر خروجی.
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: اولین تابع
Section titled “مثال ۱: اولین تابع”def greet(name): print(f"سلام {name}!")
greet("علی")greet("سارا")سلام علی!سلام سارا!def(مخفف define) یک تابع تعریف میکند: اسم، پرانتز با پارامترها، و دونقطه.- بدنهی تابع، مثل
if، تورفته است. - تعریف کردن تابع اجرایش نمیکند. خطهای
greet("علی")آن را صدا میزنند (call) و هر بار بدنه با یک مقدار متفاوت برایnameاجرا میشود. nameدر تعریف یک پارامتر است و"علی"در صدا زدن یک آرگومان.
مثال ۲: برگرداندن نتیجه، return
Section titled “مثال ۲: برگرداندن نتیجه، return”def area(width, height): return width * height
a = area(3, 4)print(a)print(area(10, 2) + area(1, 1))1221return مقدار را به جایی که تابع صدا زده شده برمیگرداند؛ انگار area(3, 4) با 12 جایگزین شده. برای همین میشود نتیجه را در متغیر گذاشت یا در یک حساب دیگر به کار برد. وقتی return اجرا شود، تابع همانجا تمام میشود.
مثال ۳: return در برابر print
Section titled “مثال ۳: return در برابر print”اشتباه شمارهی یک تازهکارها:
def add(a, b): print(a + b)
x = add(2, 3)print("مقدار x:", x)5مقدار x: Noneprint نتیجه را روی صفحه نشان داد، ولی چیزی برنگرداند. تابعی که return ندارد خودکار مقدار ویژهی None (یعنی «هیچ») را برمیگرداند، پس x خالی ماند. قاعده: تابعی که چیزی حساب میکند، return کند؛ چاپ کردن را به کسی بسپار که صدایش زده.
مثال ۴: مقدار پیشفرض و آرگومان با اسم
Section titled “مثال ۴: مقدار پیشفرض و آرگومان با اسم”def backup_name(filename, ext="bak", stamp=False): name = f"{filename}.{ext}" if stamp: name = "2026-10-21_" + name return name
print(backup_name("app.conf"))print(backup_name("app.conf", "old"))print(backup_name("app.conf", stamp=True))print(backup_name(stamp=True, filename="db.sql", ext="gz"))app.conf.bakapp.conf.old2026-10-21_app.conf.bak2026-10-21_db.sql.gzext="bak"یعنی «اگر کسی مقدار نداد،bakباشد». پارامترهای پیشفرضدار اختیاریاند.- آرگومانها را میشود به ترتیب داد (
"app.conf", "old") یا با اسم (stamp=True). با اسم، ترتیب مهم نیست و خواندن کد خیلی راحتتر است:stamp=Trueخودش میگوید یعنی چه، ولی یکTrueتنها نه. - پارامترهای بدون پیشفرض باید قبل از پیشفرضدارها بیایند.
مثال ۵: چند مقدار برگرداندن و docstring
Section titled “مثال ۵: چند مقدار برگرداندن و docstring”def stats(nums): """میانگین، کمترین و بیشترین یک لیست عددی را برمیگرداند.""" return sum(nums) / len(nums), min(nums), max(nums)
avg, low, high = stats([4, 8, 15, 16, 23, 42])print(avg, low, high)help(stats)18.0 4 42Help on function stats in module __main__:
stats(nums) میانگین، کمترین و بیشترین یک لیست عددی را برمیگرداند.return a, b, cدر واقع یک tuple سهتایی برمیگرداند، وavg, low, high = ...آن را باز میکند و هر عضو را در یک متغیر میگذارد.- رشتهای که در خط اول بدنه با سه کوتیشن (
"""...""") میآید docstring است: توضیح تابع، کهhelp()و ویرایشگرها نشانش میدهند. برای هر تابعی که دیگران (یا خودت ماه بعد) استفاده میکنند، یک docstring کوتاه بنویس.
def f(): return 1, 2 ؛ بعد از a, b = f() مقدار b چیست؟
جواب درست: 2
تابع یک tuple دوتایی برمیگرداند و = آن را در a و b باز میکند.
مثال ۶: تابع روی دادهی واقعی
Section titled “مثال ۶: تابع روی دادهی واقعی”def total_price(items): total = 0 for item in items: total += item["price"] * item["qty"] return total
cart = [ {"name": "کتاب", "price": 120000, "qty": 2}, {"name": "خودکار", "price": 15000, "qty": 5},]print(f"جمع سبد: {total_price(cart):,} تومان")جمع سبد: 315,000 تومانتابع لیستی از دیکشنریها (درس قبل) میگیرد و یک عدد برمیگرداند. حالا هر جای برنامه که جمع سبد لازم است، فقط total_price(cart) مینویسی؛ و اگر روزی مالیات اضافه شد، فقط داخل همین تابع را عوض میکنی.
مثال ۷: تابعهایی که تابع صدا میزنند
Section titled “مثال ۷: تابعهایی که تابع صدا میزنند”def is_even(n): return n % 2 == 0
def first_even(nums): for n in nums: if is_even(n): return n return None
print(first_even([3, 7, 10, 12]))print(first_even([1, 3, 5]))10Noneبرنامههای خوب از تابعهای کوچک با اسمهای گویا ساخته میشوند: if is_even(n) تقریباً مثل یک جملهی انگلیسی خوانده میشود. return n داخل حلقه هم حلقه و هم تابع را با هم تمام میکند؛ برای همین 12 هیچوقت بررسی نشد. اگر حلقه بدون پیدا کردن تمام شود، return None آخر اجرا میشود.
مثال ۸: دامنهی متغیرها
Section titled “مثال ۸: دامنهی متغیرها”count = 10
def show(): count = 99 print("داخل تابع:", count)
show()print("بیرون تابع:", count)داخل تابع: 99بیرون تابع: 10متغیری که داخل تابع ساخته میشود محلی (local) است: فقط داخل همان تابع وجود دارد و با تمام شدن تابع از بین میرود. count = 99 یک متغیر جدید داخل تابع ساخت و به count بیرونی دست نزد. این ویژگی خوبی است: تابعها نمیتوانند تصادفاً متغیرهای بقیهی برنامه را خراب کنند. قاعده: داده را با پارامتر به تابع بده و با return پس بگیر؛ به متغیرهای بیرونی تکیه نکن.
مثال ۹: lambda، تابع یکخطی برای sorted
Section titled “مثال ۹: lambda، تابع یکخطی برای sorted”گاهی فقط یک تابع کوچک و یکبارمصرف لازم است؛ مثلاً برای گفتن به sorted که «بر اساس چه چیزی مرتب کن»:
servers = [{"name": "web1", "disk": 72}, {"name": "db1", "disk": 91}, {"name": "web2", "disk": 95}]by_disk = sorted(servers, key=lambda s: s["disk"], reverse=True)print([s["name"] for s in by_disk])['web2', 'db1', 'web1']lambda s: s["disk"] یعنی «تابعی که s میگیرد و s["disk"] را برمیگرداند»؛ همان def get_disk(s): return s["disk"] در یک خط. sorted آن را روی هر عضو صدا میزند و بر اساس نتیجه مرتب میکند. برای هر چیزی بزرگتر از یک عبارت، همان def معمولی بهتر است.
مثال ۱۰: راهنمای نوع (type hint)
Section titled “مثال ۱۰: راهنمای نوع (type hint)”میتوانی بنویسی هر پارامتر و خروجی چه نوعی باید باشد:
def celsius_to_f(c: float) -> float: return c * 9 / 5 + 32
for c in [0, 25, 37, 100]: print(f"{c}°C = {celsius_to_f(c):.1f}°F")0°C = 32.0°F25°C = 77.0°F37°C = 98.6°F100°C = 212.0°Fc: float و -> float را پایتون اجبار نمیکند؛ فقط توضیحاند. ولی ویرایشگرها و ابزارهای بررسی کد از آنها استفاده میکنند تا اشتباهها را قبل از اجرا نشانت بدهند. در کدهای جدید زیاد میبینیشان.
پشت پرده: صدا زدن تابع چه میکند؟
Section titled “پشت پرده: صدا زدن تابع چه میکند؟”وقتی area(3, 4) را صدا میزنی، پایتون یک قاب (frame) تازه در حافظه میسازد: یک فضای خصوصی برای متغیرهای همین اجرا (width=3، height=4). بدنه در همین قاب اجرا میشود؛ با return، مقدار به قاب صدازننده برمیگردد و قاب تابع پاک میشود. برای همین متغیرهای محلی بیرون دیده نمیشوند، و برای همین هر صدا زدن مستقل از قبلی است. آن Traceback که در پیام خطاها میبینی، دقیقاً فهرست همین قابهاست: «از خط ۱۰ تابع main صدا زده شد، که از خط ۴ تابع load را صدا زد، که آنجا خطا رخ داد».
یک نکتهی ظریف: مقدارها با اشاره به تابع داده میشوند (مثل b = a در درس قبل). پس اگر لیستی به تابع بدهی و تابع append کند، لیست بیرونی هم عوض میشود. همین منشأ تلهی اشتباه رایج ۵ است.
جدولهای مرجع
Section titled “جدولهای مرجع”| شکل | معنی |
|---|---|
def f(a, b): |
تعریف تابع با دو پارامتر |
return x |
برگرداندن x و تمام کردن تابع |
def f(a, b=10): |
b اختیاری با پیشفرض ۱۰ |
f(1, b=5) |
آرگومان با اسم |
return a, b و x, y = f() |
چند مقدار |
"""توضیح""" |
docstring (خط اول بدنه) |
lambda x: x * 2 |
تابع یکخطی بیاسم |
def f(x: int) -> str: |
راهنمای نوع |
| خطا | معنی |
|---|---|
NameError: name 'f' is not defined |
تابع قبل از تعریفش صدا زده شده (یا غلط تایپی) |
TypeError: f() missing 1 required positional argument |
آرگومان کم دادهای |
TypeError: f() takes 2 positional arguments but 3 were given |
آرگومان زیاد دادهای |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) صدا زدن تابع قبل از تعریفش
Section titled “۱) صدا زدن تابع قبل از تعریفش”print(square(4))
def square(n): return n * nTraceback (most recent call last): File "/home/ali/py/order.py", line 1, in <module> print(square(4)) ^^^^^^NameError: name 'square' is not definedپایتون از بالا به پایین اجرا میکند و در خط ۱ هنوز def را ندیده. راهحل: تعریفها را بالای فایل بگذار.
۲) آرگومان کم
Section titled “۲) آرگومان کم”def area(width, height): return width * height
print(area(3))Traceback (most recent call last): File "/home/ali/py/args.py", line 4, in <module> print(area(3)) ^^^^^^^TypeError: area() missing 1 required positional argument: 'height'پیام دقیقاً میگوید کدام پارامتر جا افتاده. راهحل: همهی پارامترهای بدون پیشفرض را بده.
۳) print بهجای return
Section titled “۳) print بهجای return”مثال ۳: نتیجه None میشود. راهحل: تابعی که چیزی حساب میکند، return کند.
۴) فراموش کردن پرانتز هنگام صدا زدن
Section titled “۴) فراموش کردن پرانتز هنگام صدا زدن”greet (بدون پرانتز) تابع را صدا نمیزند؛ فقط به خود تابع اشاره میکند. print(greet) چیزی مثل <function greet at 0x7f...> چاپ میکند. راهحل: greet("علی").
۵) لیست بهعنوان مقدار پیشفرض
Section titled “۵) لیست بهعنوان مقدار پیشفرض”def add_item(item, basket=[]): basket.append(item) return basket
print(add_item("سیب"))print(add_item("موز"))['سیب']['سیب', 'موز']انتظار داشتی دومی فقط ['موز'] باشد! مقدار پیشفرض یک بار، هنگام تعریف تابع، ساخته میشود؛ پس همهی صدا زدنها همان یک لیست را میبینند. راهحل: پیشفرض را None بگذار و داخل تابع لیست تازه بساز:
def add_item(item, basket=None): if basket is None: basket = [] basket.append(item) return basket
print(add_item("سیب"))print(add_item("موز"))['سیب']['موز']تابع circle_area(r) بنویس که مساحت دایره (3.14159 × r × r) را برگرداند. با سه شعاع مختلف صدایش بزن و نتیجه را با دو رقم اعشار چاپ کن.
دیدن جواب
def circle_area(r): return 3.14159 * r * r
for r in [1, 2.5, 10]: print(f"شعاع {r}: {circle_area(r):.2f}")شعاع 1: 3.14شعاع 2.5: 19.63شعاع 10: 314.16(در درس ماژولها میبینی math.pi عدد دقیقتر پی را دارد.)
تمرین اصلی: تابع password_strength(pw) بنویس که یک رمز بگیرد و یکی از سه متن "ضعیف"، "متوسط" یا "قوی" را برگرداند: قوی اگر دستکم ۱۲ حرف باشد و دستکم یک رقم داشته باشد؛ متوسط اگر دستکم ۸ حرف باشد؛ وگرنه ضعیف. با چند رمز امتحانش کن. (راهنما: any(ch.isdigit() for ch in pw) میگوید آیا دستکم یک رقم هست.)
دیدن جواب
def password_strength(pw): """قدرت رمز را با سه سطح برمیگرداند.""" has_digit = any(ch.isdigit() for ch in pw) if len(pw) >= 12 and has_digit: return "قوی" elif len(pw) >= 8: return "متوسط" else: return "ضعیف"
for pw in ["1234", "sunshine", "correcthorse", "correcthorse9"]: print(pw, "→", password_strength(pw))1234 → ضعیفsunshine → متوسطcorrecthorse → متوسطcorrecthorse9 → قویcorrecthorse دوازده حرف دارد ولی رقم ندارد، پس شرط and غلط شد و به elif رسید.
تابع top_n(counts, n=3) بنویس که یک دیکشنری شمارش (مثل {"200": 50, "404": 7, ...}) بگیرد و n کلید پرتکرار را به شکل لیستی از tupleها برگرداند. پیشفرض ۳ باشد. یک بار بدون n و یک بار با n=1 صدایش بزن.
دیدن جواب
def top_n(counts, n=3): """n کلید پرتکرار را به ترتیب نزولی برمیگرداند.""" ordered = sorted(counts.items(), key=lambda kv: kv[1], reverse=True) return ordered[:n]
codes = {"200": 50, "404": 7, "500": 2, "301": 12, "403": 1}print(top_n(codes))print(top_n(codes, n=1))[('200', 50), ('301', 12), ('404', 7)][('200', 50)]ordered[:n] یعنی «n عضو اول» (برش از ابتدا). چون n پیشفرض دارد، صدا زدن بدون آن هم کار میکند.
آزمونک
Section titled “آزمونک”تعریف یک تابع با def...
جواب درست: فقط تعریفش میکند؛ با صدا زدن f() اجرا میشود
بدنه فقط هنگام صدا زدن اجرا میشود.
تابعی که return ندارد چه برمیگرداند؟
جواب درست: None
print روی صفحه مینویسد ولی چیزی برنمیگرداند.
در def f(a, b=2): کدام صدا زدن خطا میدهد؟
جواب درست: f()
a پیشفرض ندارد و باید داده شود.
متغیری که داخل تابع ساخته شده…
جواب درست: فقط داخل همان تابع وجود دارد
محلی است و با تمام شدن تابع پاک میشود.
sorted(items, key=lambda x: x[1]) بر اساس چه چیزی مرتب میکند؟
جواب درست: عضو دوم (اندیس ۱) هر item
lambda برای هر عضو مقدار مرتبسازی را برمیگرداند.
چرا def f(x, items=[]) خطرناک است؟
جواب درست: لیست پیشفرض یک بار ساخته میشود و بین همهی صدا زدنها مشترک است
پیشفرض را None بگذار و داخل تابع لیست بساز.
جمعبندی
Section titled “جمعبندی”def اسم(پارامترها):+ بدنهی تورفته؛ بااسم(آرگومانها)صدا زده میشود.returnمقدار را برمیگرداند و تابع را تمام میکند؛ بدون آن، تابعNoneبرمیگرداند.printفقط نمایش است.- مقدار پیشفرض (
b=10) پارامتر را اختیاری میکند؛ آرگومان با اسم (stamp=True) کد را خوانا. return a, bوx, y = f()برای چند مقدار؛ docstring برای توضیح.- متغیرهای داخل تابع محلیاند؛ داده را با پارامتر بده و با
returnبگیر. lambdaبرای تابعهای یکخطی (مثلkeyدرsorted)؛ هرگز لیست را پیشفرض نگذار.
| دستور | کاری که میکند |
|---|---|
def f(a, b=1): | تعریف با پیشفرض |
return a + b | برگرداندن نتیجه |
x = f(2, b=3) | صدا زدن با آرگومان با اسم |
return a, b / x, y = f() | چند مقدار |
"""توضیح تابع""" | docstring |
help(f) | دیدن docstring |
lambda s: s["disk"] | تابع یکخطی |
sorted(x, key=..., reverse=True) | مرتبسازی با کلید |
def f(x=None): if x is None: x = [] | جایگزین امن برای پیشفرض لیست |