کد را یک بار بنویسید، بارها استفاده کنید
تابع یک تکه کد نامدار است که ورودی (پارامتر) میگیرد و خروجی برمیگرداند. هر وقت دیدید یک منطق را دو بار کپی کردهاید، وقت ساختن تابع است. تابع سه مزیت دارد: تکرار را حذف میکند، به منطق یک نام میدهد (که خودش مستندسازی است) و آزمودن را ممکن میکند.
def rug_price(width_cm, length_cm, price_per_sqm):
area = width_cm * length_cm / 10_000
return round(area * price_per_sqm)
p = rug_price(200, 300, 9_500_000)
print(f"{p:,} ریال") # 57,000,000 ریال
اجزا: کلمهی def، نام تابع (snake_case)، پارامترها در پرانتز، دونقطه و بدنهی تورفته. return اجرای تابع را همان لحظه تمام میکند و مقدار را به فراخواننده برمیگرداند. تابعی که return ندارد (یا return خالی دارد) در واقع None برمیگرداند.
print در برابر return
اشتباه رایج تازهکارها این است که بهجای return، نتیجه را داخل تابع print میکنند. print فقط روی صفحه نشان میدهد و مقدار را به برنامه برنمیگرداند؛ نمیتوانید نتیجه را جمع بزنید، ذخیره کنید یا تست کنید. قاعده: توابع محاسبه return میکنند؛ فقط لایهی نمایش print میکند.
return زودهنگام و چند مقدار
def shipping_cost(area, city):
if city == "کاشان":
return 0 # حالت خاص را زود رد کن
if area >= 12:
return 0
return 350_000 if area >= 6 else 600_000
def min_max(values):
return min(values), max(values) # در واقع یک tuple
low, high = min_max([12, 6, 30])
الگوی «نگهبان» (guard clause) با return زودهنگام، کد را از if های تودرتو نجات میدهد.
پارامتر پیشفرض
def price_with_tax(amount, rate=0.10):
return round(amount * (1 + rate))
price_with_tax(1_000_000) # با نرخ پیشفرض
price_with_tax(1_000_000, rate=0.09) # با نام، خواناتر
دام معروف: پیشفرض تغییرپذیر
def add_item(item, basket=[]): # اشتباه!
basket.append(item)
return basket
print(add_item("K1")) # ['K1']
print(add_item("K2")) # ['K1', 'K2'] — سبد قبلی هنوز آنجاست!
مقدار پیشفرض فقط یک بار، هنگام تعریف تابع ساخته میشود، نه در هر فراخوانی. پس همهی فراخوانیها یک لیست مشترک دارند. الگوی درست استفاده از None است:
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
نکتههایی که کمتر کسی میداند
- همین «یکبار ساخته شدن» برای
def log(msg, when=datetime.now())هم صدق میکند: همهی لاگها زمان بارگذاری ماژول را میگیرند. زمان را داخل تابع حساب کنید. - در Ruff و Pylint قاعدهای به نام B006 یا dangerous-default-value همین دام را خودکار پیدا میکند؛ روشنش کنید.
- تابع هم یک شیء است:
rug_price.__name__نامش را میدهد و میتوانید آن را در متغیر، لیست یا دیکشنری بگذارید (درس آخر همین فصل). - بدنهی تابعی را که هنوز ننوشتهاید با
...پر کنید؛ کد اجرا میشود و یادتان میماند که جای خالی دارد. - پایتون overloading ندارد: اگر دو تابع همنام تعریف کنید، دومی بیصدا اولی را جایگزین میکند. Ruff این را هم با قاعدهی F811 تشخیص میدهد.