نقش PEP 8 در برنامه‌نویسی پایتون

PEP 8 style guideبه نظرم امروزه قبل از شروع هر کاری نیازه تا ما با اصول و قاعده اون کار آشنا باشیم. آشنایی و انجام اصول و استاندارهای هر کاری می‌تونه کمک زیادی به ما کنه تا هم کارمون رو به بهترین نحو انجام بدیم و هم کاری رو انجام داده باشیم تا هر شخصی در هر جا با دیدن کارمون متوجه عملکردمون بشه.تو دنیای برنامه نویسی هم همینطوره، شما فارغ از اینکه با چه زبان برنامه‌نویسی‌ای کار می‌کنین باید به این اصول و استانداردها برای کدنویسی آشنا باشید تا کدی در سطح جهانی بنویسید.این کار، هم به پیشرفت شغلی شما کمک می‌کنه هم به شخص دیگه‌ای که به کدتون نگاه می‌کنه یا حتی بجای شما و یا کنار شما مشغول بکار می‌شه، بتونه درک درستی از فرآیند کدنویسی داشته باشه.درنتیجه رعایت اصول کدنویسی و همینطور کدنویسی تمیز(clean code) لازمه هر برنامه‌نویسیه که می‌خواد تو این اکوسیستم فعالیت کنه.تو این نوشته می‌خوایم راجع به شیوه‌نامه نگارشی به نام PEP تو پایتون رو باهم مرور کنیم.PEP مخفف عبارت Python Enhancement Proposal به معنیه پیشنهادی برای بهبود پایتونه که یک سری افراد برای رسیدن به یک استاندارد جهانی و کدهای تمیز و خوانا، شیوه و نحوه نگارشی رو تعیین کردن.هر PEP به همراه خودش یک شماره رو به عنوان نام دارد.به نظرم دو تا از مهم‌ترین و کاربردی‌ترین PEPهای پایتون PEP 8 و PEP 20 است که تو این مقاله به PEP 8 می‌پردازیم.بررسی PEP 8:PEP 8 توسط خالق پایتون Guido van Rossum تو سال 2001 الی 2013 نوشته شده و در حال استفاده است.این داکیومنت قوانین کدگذاری کد پایتون رو ارائه می‌ده. یکی از دیدگاه‌هایی که Guido van Rossum داره اینه که، کد خیلی بیشتر از اینکه نوشته بشه خونده می‌شه.در نتجه دستورالعمل‌هایی که تو PEP 8 ارائه شده، به بهبود خوانایی و هماهنگی کد در سطح گسترده، در نظر گرفته شده.نکته مهمی که PEP 8 بهش اشاره می‌کنه اینه که سازگاری کدها مهمه و میگه تا جایی که می‌شه باید از این شیوه‌نامه پیروی کرد.نکته‌ای که هست اینه که تو بعضی از مواقع نمی‌شه از این قواعد پیروی کرد؛ برای همین، تو این مواقع راه‌حلی که داره به ما پیشنهاد می‌شه اینه که بیایم کدهای دیگران رو ببینیم که تو این مواقع چیکار کردن و یا اینکه اون چیزی رو که فکر می‌کنیم بهتره رو انجام بدیم و نکته مهم‌تری که بهش اشاره داره اینه که از پسیدن دریغ نکنیم.پرسیدن می‌تونه سرچ‌کردن هم باشه، پس سرچ‌کرن یادمون نره.مواردی که PEP 8 به اون اشاره می‌کنه:برای یادگرفتن کامل PEP می‌تونید به داکیومنت PEP سر بزنید ولی خوندن این نوشته هم خالی از لطف نیست.تب یا اسپیس؟ مسئله این است:برای تورفتگی ترجیحا از اسپیس استفاده بشه و تب‌ها صرفا باید زمانی استفاده بشه که تو رفتگی‌هایی با استفاده از تب‌ها داشته باشیم.پایتون این اجازه رو به ما نمی‌ده تا از ترکیب تب و اسپیس استفاده کنیم.برای تو رفتگی از 4 تا اسپیس استفاده کنین.حداکثر طول خط:در PEP 8 حداکثر طول هر خط به 79 کاراکتر محدود شده و برای فرمت تکست‌ها و یا داک استرینگ‌ها حداکثر 72 کاراکتر است.قراردادهای نام‌گذاری:کلاس ها معمولاً باید با استفاده از قرارداد PascalCase نوشته بشن؛ طوری که حروف اول کلمات با حرف بزرگ و بقیه حروف با حرف کوچک و بدون استفاده از فاصله یا underscore.# Correct: class MyClass: pass # Wrong: class my_Class: passماژول‌ها، تابع‌ها و متغیرها باید با توجه به قرارداد snake_case بصورت حروف کوچک و بین هر کلمه با underscore برای جداسازی نوشته بشن.# Correct: import module_name def function_name(): pass full_name = 'Milad Mahmoodi' # Wrong: import Modulename def FunctionName(): pass FullName = 'Milad Mahmoodi'نام‌گذاری اکسپشن‌ها هم مثل کلاس‌ها بصورت PascalCase است.# Correct: try: pass except ValueError: pass # Wrong: try: pass except valueError: passثابت‌ها(Constants) کاملا با حروف بزرگ بصورت CAP_WORD نوشته و با استفاده از underscore از هم جدا می‌شن.# Correct: PI = 3.14 GRAVITY = 9.8 GRAVITY_ACCEL = 9.8 # Wrong: pi = 3.14 Gravity = 9.8 GRAVITYACCEL = 9.8پکیج‌ها باید با حروف کوچیک باشن و نمی‌تونیم از underscore برای جداسازی کلمات استفاده کنیم.# Correct: pandas matplotlib # Wrong: Pandas DateTimeفضای خالی(فاصله) در دستورات و عبارات:همونطور که تو مثال‌های قبل دیدید تو زمان تعریف متغیرها، فانکشن‌ها و ... از فاصله استفاده شده.برای مثال:full_name = 'Milad Mahmoodi' PI = 3.14 a = 2 + 1اینجا ما برای تعریف متغیر و ثابت‌ها، قبل و بعد از عملگر انتساب و تو خط آخر هم برای عملگر انتساب و هم برای عملگر جمع نیز یک فاصله گذاشتیم.بعضی از مواقع نباید تو کدنویسی‌مون از فاصله استفاده کنیم؛ در ادامه چند مورد از نبایدها رو نام می‌بریم و با نحوه درستش مقایسه می‌کنیم.بعد از پرانتز، براکت‌ها و بریس‌ها:# Correct: spam(ham[1], {eggs: 2}) # Wrong: spam( ham[ 1 ], { eggs: 2 } ) #...به فاصله بین پرانتزها، براکت‌هاو بریس‌ها دقت کنینبین آخرین کاما و آخرین پرانتز:# Correct: foo = (0,) # Wrong: bar = (0, ) #...به فاصله بین کاما و پرانتز بسته دقت کنینقبل از کاما، سمیکولون یا کولون:# Correct: if x == 4: print(x, y); x, y = y, x # Wrong: if x == 4: print(x , y); x, y = y, xاسلایس‌بندی لیست‌ها:# Correct: ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] # Wrong: ham[1: 9], ham[1 :9], ham[1:9 :3]قبل از پرانتز باز که لیست آرگومان تابع رو فراخوانی می‌کنه:# Correct: spam(1) # Wrong: spam (1)قبل از براکت باز که شروع به اسلایس‌بندی یا ایندکس‌گذاری می‌کنه:# Correct: dct['key'] = lst[index] # Wrong: dct ['key'] = lst [index]بیشتر از یک فاصله دور عملگرها برای ترازبندی آن‌ها با همدیگه:# Correct: x = 1 y = 2 long_variable = 3 # Wrong: x = 1 y = 2 long_variable = 3خطوط خالی:در زمان تعریف کلاس‌‌ها و متدها باید به خط خالی برای آن‌ها توجه کرد:در زمان تعریف کلاس باید دو خط خالی قبل از تعریف و دو خط خالی بعد از تعریف کلاس در نظر بگیرید.برای متدهایی که تو کلاس تعریف می‌شن باید از بالا و پایین یک خط خالی قرار بدید.بد نیست به این توصیه‌ها هم توجه کرد:از دنباله‌دار کردن فضای خالی تو هر نقطه خودداری کنین.همیشه عملگرها رو با یک فاصله تو دو طرف آن احاطه کنین.اگر از عملگرهایی با اولویت‌های متفاوت استفاده می‌کنین، برای دور عملگرهایی با کمترین اولویت، فضای خالی در نظر بگیرید.# Correct: i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b) # Wrong: i=i+1 submitted +=1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b)طرح بندی کد(Code Lay-out):تورفتگی:خط‌هایی که ادامه‌دار هستن و یا مثل تابع‌هایی که آرگومان دارن باید بصورت عمودی با استفاده از خط ضمنی پایتون که در داخل پرانتز، براکت و بریس‌ها به هم متصل می‌شن، ترازبندی بشن و یا با استفاده از یک hanging indent، تراز بشن.برای تو رفتگی از 4 تا اسپیس استفاده کنین.ترازبندی hanging indent: یک سبک type-setting است که تو اون پرانتز باز یه عبارت، آخرین کاراکتر بدون فاصله خط است و خطوط بعدی تا پرانتز بسته تورفتگی داره.# Aligned with opening delimiter. foo = long_function_name(var_one, var_two, var_three, var_four) # Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest. def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # Hanging indents should add a level. foo = long_function_name( var_one, var_two, var_three, var_four) # Wrong: # Arguments on first line forbidden when not using vertical alignment. foo = long_function_name(var_one, var_two, var_three, var_four) # Further indentation required as indentation is not distinguishable. def long_function_name( var_one, var_two, var_three, var_four): print(var_one)پرانتز، براکت و بریس بسته در ساختارهای چندخطی ممکنه زیر اولین کاراکتر بدون فضا خالی(non-whitespace) آخرین خط لیست قرار بگیره:my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )یا ممکنه زیر اولین کاراکتر خطی که ساختار چند خطی رو شروع می‌کنه، ردیف بشه:my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )تورفتگی در زمان استفاده از عملگرها:به کد پایین توجه کنین!# Wrong: # operators sit far away from their operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest) # Correct: # easy to match operators with operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)در کد بالا به عملگرهای بعد از متغیر‌ها توجه کنین! تو حالت اول بعد از متغیر‌ها از عملگرها استفاده شده، که تو این حالت باید به دنبال این باشیم که ببینیم چه متغیری از چه عملگری استفاده کرده ولی تو روش دوم این مشل حل شده و ما با یک نگاه ساده متوجه می‌شیم که چه متغیری از چه عملگری استفاده کرده.زمان استفاده از کاماهای دنباله‌دار:اکثرا وقتی که از سیستم کنترل ورژن استفاده می‌شه کاماهای اضافی انتهایی مفیدن؛ انتظار می‌ره لیست مقادیر، آرگومان‌ها یا ورودی‌ها(اینپوت‌ها) تو طول پروژه توسعه داده بشن؛ درنتیجه ما با این الگو که هر مقدار(متغیر‌ها، آرگومان‌ها، ورودی‌ها) رو به تنهایی روی یک خط قرار می‌دهم و همیشه یک کاما اضافی رو تو آخر خط قرار می‌دیم و پرانتز، براکت یا بریس بسته رو تو خط بعدی وارد می‌کنیم.# Correct: FILES = [ 'setup.cfg', 'tox.ini', ] initialize(FILES, error=True, ) # Wrong: FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)کامنت ها:بدتر از ننوشتن کامنت، نوشتن کامنتیه که با کد همخونی نداره. درنتیجه فکری به حال آپدیت کردن کامنت‌هاتون بکنین.کامنت‌ها باید جمله‌های کاملی باشن. کلمه اول باید با حروف بزرگ نوشته بشن، مگه اینکه علامتی(شناسه) باشه که باید خود علامت رو نوشت(هرگز حروف علامت‌ها رو تغییر ندید!).کدنویسان عزیزی که پایتون می‌نویسید، اگه انگلیسی زبان نیستین لطفا کامنت‌های خودتون رو به زبان انگلیسی بنویسید، مگه اینکه 120% مطمئن باشین که این کد هرگز توسط افرادی غیر از شما و هم زباناتون خونده نمی‌شه.ایمپورت کردن:معمولا ایمپورت‌ها رو باید تو خط‌های جداگانه‌ای نوشت.# Correct: import os import sys from subprocess import Popen, PIPE # Wrong: import sys, osایمپورت کردن باید طبق دسته‌بندی زیر انجام بشه:Standard library imports.Related third party imports.Local application/library specific importsدر نهایت باید یک خط خالی بین هر گروه از ایمپورت‌ها قرار بدید.توصیه‌های برنامه‌نویسی:کد باید طوری نوشته بشه که به پیاده‌سازی‌های دیگه پایتون آسیبی وارد نکنه مثل(PyPy, Jython, IronPython ...) مثلا، برای جملاتی مثل a += b یا a = a + b به CPython برای جفت شدن رشته‌ها اعتماد نکنین. این بهینه‌سازی حتی تو CPython هم قابل اعتماد نیست(فقط برای یکسری از نوع‌ها کار می‌کنه).تو بخش‌هایی که عملکرد حساسی دارن از متدjoin().''باید به جای a += b یا a = a + b استفاده بشه. این متد به ما این اطمینان رو می‌ده که الحاق انجام خواهد شد.مقایسه با singletonهایی مانند None همیشه باید با is یا is not انجام بشه و هرگز نباید با عملگرهای مقایسه‌ای انجام بشه. مانند a is b که نشون میده هر دو متغیر از یک شی یکسان هستند:# Correct: a = None if a is None: pass # Wrong: if a == None: passبرای عملیات‌های مقایسه‌ای تو کلاس‌ها بهتره از متد‌های (__eq__, __le__, __ne__, __lt__, __ge__, __gt__) استفاده کنین. که برای راحت‌تر شدن این موضوع ابزاری به نام ()functools.total_ordering ارائه شده است.همیشه بجای دستور انتساب که یک عبارت lambda رو مستقیماً به یک شناسه متصل می‌کنه، از دستور def استفاده کنین:# Correct: def f(x): return 2*x # Wrong: f = lambda x: 2*xروش اول بطور کلی برای ردیابی و نمایش رشته‌ها مفیدتره. استفاده از دستور انتساب، تنها مزیتی رو که یه عبارت لامبدا می‌تونه نسبت به یه فانکشن ارائه بده رو حذف کنه(یعنی اینکه می‌شه اون رو تو یه عبارت بزرگ‌تر جاسازی کرد).از ()startswith.' ' و ()endswith.' ' به جای اسلایس کردن رشته برای چک کردن پیشوند یا پسوند استفاده کرد که تمیزتر و خطا کمتری دارن:# Correct: if foo.startswith('bar'): pass # Wrong: if foo[:3] == 'bar': passبرای مقایسه نوع شی، همیشه باید به جای مقایسه مستقیم، از متد ()isinstance استفاده کرد:# Correct: if isinstance(obj, int): pass # Wrong: if type(obj) is type(1): passبرای دنباله‌ها(رشته‌ها، لیست‌ها و تاپل‌ها) به این نکته توجه کنین که دنباله‌های بدون عضو، نادرست(False) و دنباله‌های دارای عضو، درست(True) هستن:# Correct: seq = list() if not seq: pass if seq: pass # Wrong: if len(seq): pass if not len(seq): passمقادیر بولی(boolean) رو با استفاده از عملگر مقایسه‌ای، مقایسه نکنین:# Correct: if greeting: pass # Wrong: if greeting == True: pass if greeting is True: passاستفاده از else در مواقع‌ای که حلقه‌های for و while نتیجه‌ای ندارن:ls = [1, 2, 3] for item in ls: if ls == 5: pass else: passدر آخر ممنونم از اینکه وقت گذاشتید و نوشته‌ام رو خوندید. سعی کردم تا اونجایی که تونستم اون‌چیزهایی رو که در مورد PEP 8 خوندم رو با شما به اشتراک بذارم.خیلی خوشحال می‌شم نظراتتون رو داشته باشم.