بنده در دانشگاه هرمزگان دستیار تدریس یا حل التمرین(TA) برنامهسازی پیشرفته هستم بودم. ضمن تصحیح پروژه دانشجویان به اشتباهاتی درست(!) در کد هایشان برخوردم. قسمت درست کدها این میباشد که «کار» میکنند اما به روش درست پایتون نوشته نشدهاند.
اول: عدم استفاده از unpack
تکه کد زیر را در نظر بگیرید:
item = line.rstrip().split("-")
name = item[0]
amount = int(item[1])
price = int(item[2])
description = item[3]
این کد میتوانست بهتر و به یکی از صورتهای زیر نوشته شود:
item = line.rstrip().split("-")
name, amount, price, description = item
amount = int(amount)
price = int(price)
item = line.rstrip().split("-")
name, amount, price, description = (int(x) if i in (1, 2) else x for i, x in enumerate(item))
برای راهحلهای بیشتر برای حل همچین مشکلی این سوال استکآورفلو را ببینید.
دوم: عدم استفاده از dataclass
در پایتون دیکوریتوری(decorator) به اسم dataclass داریم. همانطور که از اسمش پیداست برای نوشتن کلاسهای داده بسیار مناسب است. کلاس زیر را در نظر بگیرید:
class Item:
def __init__(self, name, amount, price, description):
self.name = name
self.amount = amount
self.price = price
self.description = description
برنامهنویس میتوانست با استفاده از dataclass آنرا بنویسد:
from dataclasses import dataclass
@dataclass
class Item:
name: str
amount: int
price: int
description: str
سوم: تابع بیشتر از یک وظیفه به عهده دارد
تابع زیر را در نظر بگیرید:
def f(x: str):
if x.isdigit():
x = int(x)
return x ** 2
else:
raise ValueError("digits only")
بهتر نیست از همان اول تابع تنها یک کار «مربع کردن ایکس» را به جای دو کار «مربع کردن ایکس» و «چک کردن اینکه ایکس عدد صحیح هست یا نه و تبدیلش به عدد صحیح» را انجام دهد؟ البته این اشتباه تنها به توابع و زبان پایتون محدود نمیشود.
چهارم: تبدیل همهچیز به لیست و توپل
این اشتباه بین تازهکاران برای مپها،دیکشنریها و متدها مرتبطشان،فیلترها و غیره به چشم میخورد. تکه کد زیر را در نظر بگیرید:
my_cool_list = list(some_dict.values())
my_new_list = [v+1 for v in my_cool_list]
میتوانست به صورت زیر نوشته شود:
my_cool_list = [v+1 for v in some_dict.values()]
پنجم: عدم پیروی از قرارداد های اسمی
هر زبانی معمولا یک(یا چند) قرارداد برای نامگذاری دارد که توسط جامعه زبان دنبال میشود و برای آنکه بین اسامی خوانا باشند و مشخص باشد که این اسم مربوط به چه چیزیست(نام تابع یا نام کلاس یا هرچیز دیگر) این قراردادها را دنبال میکنیم. در ادامه به ترتیب دو تکه کد را میبینید که اولی نامگذاری اشتباه یک تابع و کلاس سپس دومی نام گذاری درست همان تابع و کلاس را نشان میدهد. برای اطلاع از این قراردادها و البته دیگر قراردادهای موجود در پایتون، PEP8 را مطالعه کنید.
# This is wrong!
def Function(x: float):
return x**2 + 1
class my_cool_class:
...
# This is right!
def func(x: float):
return x**2 + 1
class MyCoolClass:
...
ششم: عدم قراردادن فاصله در جای مناسب
اینجا نیز پای PEP8 در میان است. به یک تکه کد کاملا اشتباه و معادل درستش بسنده میکنم.
class Core:
def __init__ (self, x,y) :
self.x= x
self.y =y
class Core:
def __init__(self, x, y):
self.x = x
self.y = y
هفتم: تعریف خصوصیت(attribute) جدید برای کلاس بعد از ایجاد
هرچند که پایتون به کد شما زمانی که اینکار را میکنید، ایراد وارد نمیکند اما بنظر شخص بنده عادت خوبی است که در متد __init__ کلاس تمام ویژگیهایی که نیاز دارید را تعریف کنید و در دیگر متدها تنها از آنها استفاده کنید. برای مثال این کد اینکار را نمیکند:
class C:
def __init__(self, x, y):
self.x = x
self.y = y**2
def m(self, z):
self.z = z
return z + 1
در کلاس C که در بالا تعریف شده است، خصوصیت z خارج از متد m تعریف شده است. در تکه کد زیر این مسئله اصلاح شده است:
class C:
def __init__(self, x, y):
self.x = x
self.y = y**2
self.z = 0
def m(self, z):
self.z = z
return z + 1
اشتباه دیگر مرتبط با این موضوع که زمان تصحیح پروژههای دانشجویان مشاهده شده است، انتصاب هر پارامتر یا آرگامونی است که در متدها دریافت میکنند(!):
class C:
def power_method(self, x, y):
self.x = x
self.y = y
return x ** y
کلام پایانی: چطور اشتباهات خود را کمتر کنیم؟
جدای از خواندن کد دیگر برنامهنویسان و خواندن best practice های هر زبانی که کار میکنید، میتوانید از نرمافزار linter استفاده کنید تا اشتباهات شما را گرفته و شاید تصحیح کند. همچنین سعی کنید «کد تمیز» بنویسید و از PEP8 تا جای ممکن پیروی کنید.
عــــالـــــی !