مقدمه¶
این شیوهنامه، نحوه نگارش کدهای پایتون و قراردادهای کدنویسی پایتون را بر اساس کتابخانه اصلی پایتون ارائه میدهد. لطفاً برای آگاهی از نحوه نگارش کدهای زبان C در هسته پایتون به شیوهنامه مرتبط با آن مراجعه کنید <7>. در این متن از واژه «پپ» (PEP) برای اشاره به استانداردهای مختلف زبان پایتون استفاده میشود، PEP مخفف عبارت Python Enhancement Proposal (طرح پیشنهادی بهبود پایتون) است.
این راهنما و پپ ۲۵۷ (قراردادهای مستندسازی) از مقاله اصلی جناب خیدو فان روسوم [خالق پایتون] درباره نحوه نگارش کدهای پایتون و همچنین راهنمای کدنویسی آقای بری ورشو [توسعهدهنده گنو Mailman] الهام گرفته شده است [2].
این شیوهنامه با گذشت زمان، پدید آمدن قراردادهای تازه و همچنین منسوخ شدن قراردادهای پیشین تکامل مییابد.
بسیاری از پروژهها راهنمای نگارش کد مخصوص به خود را دارند. در صورت بروز هر گونه تداخل بین این شیوهنامه و نحوه نگارش اختصاصی یک پروژه، راهنمای نگارش آن پروژه در اولویت است.
ثبات ناشیانه، عامل عقبماندگی مغزهای کوچک است¶
یکی از بینشهای کلیدی خیدو این است که کدها بیش از آن که نوشته شوند، خوانده میشوند. نکات مطرح شده در این شیوهنامه برای بهبود خوانایی (قابلیت خوانده شدن) کدهای پایتون و یکدست کردن آنها در طیف وسیع است. همانطور که در پپ ۲۰ گفته شده: «خوانایی مهم است».
این شیوهنامه مستقیماً با «ثبات» در نحوه نگارش کدها مرتبط است. ثبات در این شیوهنامه بسیار با اهمیت است. ثباتِ درونپروژهای از آن هم مهمتر است. ثبات درون یک ماژول یا یک تابع مهمترین اصل است.
اما بدانید که کجا بهتر است بیثبات باشید — گاهی اوقات اجرای این شیوهنامه امکانپذیر نیست. وقتی شک داشتید، وجدان خود را قاضی کنید. به دیگر نمونه کدها نگاه کنید و بر آن اساس بهترین تصمیم را بگیرید. از پرسیدن سوال نترسید!
به طور خاص: برای رعایت و اجرای این شیوهنامه، سازگاری کد با نگارشهای پیشین [نرمافزار] را بر هم نزنید!
دلایل خوب دیگری برای نادیده گرفتن اصول این شیوهنامه:
هنگامی که بکارگیری این شیوهنامه باعث شود خوانایی کد پایین بیاید، حتی زمانی که خوانندهٔ کد هم از این شیوهنامه پیروی کند.
برای یکدست بودن با دیگر کدهایی که از قبل در پروژه موجود است -- هرچند این شاید فرصتی برای پاکسازی خرابکاری دیگران هم باشد.
کدهای موجود، قبل از بوجود آمدن این شیوهنامه نوشته شدهاند و دلیل دیگری (بجز اعمال این شیوهنامه) برای بازنویسی آنها وجود ندارد.
وقتی نیاز است کدها با نگارشهای قدیمی پایتون سازگار باشند و نگارشهای قدیمی پایتون از روشهای گفته شده در این شیوهنامه پشتیبانی نکنند.
چیدمان کد¶
تورفتگی¶
برای هر پله تورفتگی از ۴ فاصله استفاده کنید.
خطوط ادامهدار بهتر است عناصر خود را به صورت عمودی همتراز کنند، این کار میتواند با چیدن عناصر زیر یکدیگر درون پرانتزها، کروشهها و براکتها، و یا استفاده از تورفتگیهای هماندازه در خطوط اضافی انجام شود [1]. هنگام استفاده از تورفتگی در خطوط ادامهدار به این نکته توجه داشته باشید که شکل ظاهری کدها باید به گونهای باشد که خوانندهٔ کد به سادگی متوجه شود این تورفتگیها مربوط به شکسته شدن یک خط بلند و ادامهدار است.
# Correct:
# 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)
رعایت قانون ۴-فاصله برای خطوط ادامهدار الزامی نیست.
اختیاری:
# Hanging indents *may* be indented to other than 4 spaces.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
زمانی که بخش شرطی یک دستور if
ترکیبی طولانی باشد، به گونهای که مجبور باشیم آن را در چند خط بنویسیم، با توجه به اینکه خود دستور if
از دو حرف تشکیل شده و پس از آن نیز یک فاصله و پرانتز قرار میگیرد، میتوانیم خطوط اضافی شرط را با رعایت ۴ فاصله زیر خط اصلی بنویسیم. گرچه این امر سبب بوجود آمدن یک تداخل بینایی میشود که در نتیجه آن شرطهای دستور if
از دستورات بلوک زیر آن به سختی قابل تفکیک خواهند بود، زیرا دستورات داخل بلوک شرطی هم باید ۴ فاصله تورفتگی داشته باشند. این شیوهنامه توصیه خاصی برای ایجاد تمایز بین شرطهای اصلی و دستورات درون بلوک شرطی ندارد. برخی گزینههای قابل قبول به شرح زیر هستند:
# No extra indentation.
if (this_is_one_thing and
that_is_another_thing):
do_something()
# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# Add some extra indentation on the conditional continuation line.
if (this_is_one_thing
and that_is_another_thing):
do_something()
(همچنین مباحث مربوط به نحوه شکستن خطوط، قبل و بعد از عملگرهای ریاضی را هم پایینتر ببینید.)
پرانتز/کروشه/براکت انتهایی در متغیرهای چندخطی میتواند زیر اولین حرف از آخرین خط نوشته شود، همانند:
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',
)
تب یا فاصله؟¶
برای ایجاد تورفتگی بهتر است از فاصله استفاده شود.
استفاده از تب تنها زمانی ارجحیت دارد که بخواهیم با کدهای از پیش موجود هماهنگ شویم، یعنی کدهای قدیمی که در آنها از تب برای ایجاد تورفتگی استفاده شده است.
پایتون ترکیب تب و فاصله برای تورفتگی را مجاز نمیداند.
بیشترین طول خط¶
بیشترین اندازه یک خط باید نهایتاً ۷۹ حرف باشد.
برای بلوکهای طولانی متن که محدودیتهای ساختاری ندارند (مثل کامنتها یا مستندات درون کد)، طول هر خط نباید از ۷۲ حرف بیشتر شود.
رعایت طول مناسب خطوط این امکان را پدید میآورد که بتوان چند فایل را به صورت همزمان در کنار یکدیگر باز کرد، همانند زمانی که از ابزارهای بررسی کد برای مقایسه تغییرات یک فایل استفاده میکنیم.
شکستن خطوط به صورت خودکار در بسیاری از نرمافزارهای کدنویسی باعث برهم خوردن ساختار کدها و فهم سختتر کد میشود. این محدودیتها برای این انتخاب شدهاند که در نرمافزارهایی که عرض پنجره آنها حداقل ۸۰ کاراکتر است، از شکستن خودکار خطوط جلوگیری شود. هرچند برخی نرمافزارهای کدنویسی به صورت کلی شکستن خطوط را اعمال نمیکنند.
برخی تیمها ممکن است اندازه خطوط طولانیتر را ترجیح دهند. در صورت توافق اعضای تیم، برای کدهایی که فقط توسط خود تیم خوانده و نگهداری میشوند، طول هر خط میتواند تا ۹۹ حرف هم در نظر گرفته شود. اما در این حالت هم تعداد حروف کامنتها و مستندات نباید از ۷۲ حرف بیشتر شود.
کتابخانه استاندارد پایتون بسیار محافظهکار است و لازم است که تمام خطوط آن کمتر از ۷۹ حرف باشند. همچنین کامنتها و مستندات باید کمتر از ۷۲ حرف باشند.
روش ترجیحی برای شکستن خطوط طولانی، استفاده از خط ادامهدار ضمنی پایتون داخل پرانتزها، کروشهها و آکولادهاست. با شکستن عبارات داخل پرانتزها، می توان خطوط طولانی را به چندین خط تقسیم کرد. این روش برای شکستن خطوط بلند، به جای استفاده از بکاسلش، اولویت دارد.
گاهی اوقات بکاسلشها میتوانند کارآمد باشند. برای مثال پیش از پایتون ۳٫۱۰، برخی از دستورات with
که ممکن است طولانی و چندگانه باشند را میتوان با کمک بکاسلش سادهتر و خواناتر نوشت.
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written', 'w') as file_2:
file_2.write(file_1.read())
(درباره عبارت with
چندخطی، میتوانید مباحث پیشین در ارتباط با دستورات شرطی چندخطی multiline if-statements مطالعه کنید.)
مورد دیگر با دستور assert
است.
مطمئن شوید که تورفتگی خطوط ادامهدار را رعایت کنید.
خطوط باید بعد از عملگرهای ریاضی شکسته شوند یا قبل از آنها؟¶
برای سالها، روش پیشنهادی این بود که خطوط بعد از عملگرهای ریاضی شکسته شوند؛ اما این کار به دو دلیل باعث پایین آمدن خوانایی کدها میشود: عملگرها در ستونهای مختلفِ کد پراکنده میشوند، و هر عملگر از دستورات مربوط به خود دور شده و در خط قبل قرار میگیرد. برای نمونه در کد زیر، چشم انسان مجبور است کار بیشتری انجام دهد تا متوجه شویم کدام عبارت قرار است اضافه شود و کدام یک کم شود:
# Wrong:
# operators sit far away from their operands
income = (gross_wages +
taxable_interest +
(dividends - qualified_dividends) -
ira_deduction -
student_loan_interest)
برای حل مشکل خوانایی کدها، جامعه ریاضیدانها روش متفاوتی برگزیدند. دانلد نات در مجموعه کتابهای کامپیوترها و حروفچینی روش سنتی را اینگونه شرح میدهد: "گرچه فرمولهای درون هر پاراگراف بعد از عملگر ریاضی شکسته میشوند، اما هنگام نمایش مناسب، این فرمولها باید قبل از عملگرها شکسته شوند" [3].
پیروی از سبک نگارش قدیمی ریاضیات معمولا باعث نتیجه بهتری در نمایش کدها میشود:
# Correct:
# easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
در کدنویسی پایتون مادامی که یکدست بودن کدها رعایت شود و همیشه از یک قاعده پیروی شود، هر دو روش فوق، مجاز و قابل استفاده هستند. هر چند برای کدهایی که تازه نوشته میشوند، پیشنهاد میشوند از روش دانلد نات (شکستن خطوط قبل از عملگر) استفاده شود.
خطوط خالی¶
قبل از تعریف توابع سطح بالا و کلاسها از دو خط خالی استفاده کنید.
قبل از تعریف متدها درون کلاس، از یک خط خالی استفاده کنید.
در مواردی معدود میتوان برای گروهبندی توابع مرتبط با هم، و جداسازی این گروهها از یکدیگر، از خطوط خالی اضافه استفاده کرد. خطوط خالی را میتوان از بین دستورات یکخطی مرتبط با هم حذف کرد (برای مثال در پیادهسازی تستهای ساختگی).
در توابع بهتر است صرفاً برای جدا کردن بخشهای منطقی، از خطوط خالی استفاده کنید.
پایتون از کاراکتر کنترلی control-L (یا L^) برای ایجاد فضای خالی استفاده میکند؛ بسیاری از ابزارها این کاراکترها را تفکیک کننده صفحه تلقی میکنند، که میتوانید از آنها برای تفکیک کردن صفحات بخشهای مرتبط با هم در فایل خود استفاده کنید. توجه کنید که بعضی از ویرایشگرها و نمایشگرهای کد مبتنی بر وب ممکن است control-L را به عنوان کاراتر کنترلی تفکیک کننده صفحه تشخیص ندهند و به جای آن یک عملکرد دیگر از خود نشان دهند.
کدبندی فایل¶
کدهای هستهٔ پایتون همیشه باید از UTF-8 استفاده کنند، و نباید خط اعلام رمزگذاری داشته باشند.
در کتابخانه استاندارد، رمزگذاریهای غیر UTF-8 فقط باید برای اهداف آزمایشی استفاده شوند. از کاراکترهای غیر ASCII کمتر استفاده کنید، ترجیحاً فقط برای نشان دادن نام مکانها و انسانها. اگر از کاراکترهای غیر ASCII به عنوان داده استفاده میکنید، از کاراکترهای یونیکد شلوغ مانند z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ و علامتهای ترتیب بایت خودداری کنید.
تمام شناسهها (نام یک متغیر، تابع، ماژول و ...) در کتابخانه استاندارد پایتون باید فقط از نوع ASCII باشند و باید تا آنجا که امکان دارد از کلمات انگلیسی استفاده کنند (در بسیاری از موارد، از برخی اختصارات و اصطلاحات فنی استفاده میشود که انگلیسی نیستند).
پیشنهاد میشود پروژههای بازمتنی که مخاطب جهانی دارند هم از سیاست مشابهی استفاده کنند.
ایمپورت¶
ایمپورتها بهتر است در خطوط جداگانه باشند:
# Correct: import os import sys
# Wrong: import sys, os
مشکلی ندارد اگر بگوییم:
# Correct: from subprocess import Popen, PIPE
ایمپورتها همیشه در ابتدای فایل نوشته میشوند، درست پس از کامنتها، مستندات ماژول و قبل از تعریف متغیرها و ثابتها.
ایمپورتها باید به ترتیب زیر گروهبندی شوند:
ایمپورتهای کتابخانه استاندارد.
ایمپورت کتابخانههای شخص ثالث.
ایمپورت کتابخانهها و کدهای محلی (local).
شما باید بین هر گروه از ایمپورتها یک خط خالی قرار دهید.
پیشنهاد می شود ایمپورتها به صورت مطلق انجام شوند، زیرا معمولا خواناتر هستند و در صورت پیکربندی نادرست سیستم ایمپورت (مانند زمانی که یک دایرکتوری داخل یک بسته به
sys.path
ختم می شود) رفتار مناسب از خود نشان می دهند ( یا حداقل پیغام های خطای بهتری نمایش می دهند):import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
با این حال، ایمپورتهای نسبی صریح، جایگزین قابل قبولی برای ایمپورتهای مطلق هستند، بخصوص زمانی که با ترتیب پیچیده بستهها سروکار داریم که استفاده از ایمپورتهای مطلق، غیرضروری، شلوغ و ناخوانا خواهد بود:
from . import sibling from .sibling import example
کدهای کتابخانه استاندارد باید از ایمپورت کردن به صورت لایههای پیچیده اجتناب کنند و همیشه از ایمپورتهای مطلق استفاده کنند.
هنگام ایمپورت یک کلاس از ماژولی که شامل کلاسهای مختلف است، میتوانیم از الگوی زیر استفاده کنیم:
from myclass import MyClass from foo.bar.yourclass import YourClass
اگر کلاسها یا توابعی که در یک فایل ایمپورت میکنید با نامهای موجود در آن فایل تداخل دارند، بهتر است آنها را صریحاً به صورت زیر بنویسید:
import myclass import foo.bar.yourclass
و از "myclass.MyClass" و "foo.bar.yourclass.YourClass" استفاده کنید.
از ایمپورتهای عمومی (استفاده از سمبلهای جمع مانند ”*” در
from <module> import *
) باید اجتناب شود، زیرا آنها باعث میشوند واضح نباشد که چه نامهایی در فضانام موجود هستند، این مسئله باعث گیج شدن خواننده و بسیاری از ابزارهای خودکار میشود. فقط یک دلیل قابل دفاع در استفاده از ایمپورتهای عمومی وجود دارد: بازنشر یک رابط داخلی به عنوان بخشی از یک API عمومی ( برای نمونه، بازنویسی یک پیادهسازی پایتون خالص از یک رابط با تعاریفی اختیاری از یک ماژول شتابدهنده و اینکه دقیقا از قبل مشخص نشده است کدام تعاریف بازنویسی میشوند).هنگام بازنشر نامها به این روش ، دستورالعملهای زیر در مورد رابطهای عمومی و داخلی همچنان اعمال میشوند.
داندرهای سطح ماژول¶
داندرها، متغیر/متدهایی هستند که نام آنها با دو زیرخط آغاز شده و با دو زیرخط خاتمه مییابد، مانند __all__
، __author__
، __version__
. داندرهای در سطح ماژول باید پس از مستندات ماژول، و قبل از ایمپورتها نوشته شوند، به استثنای ایمپورتهای from __future__
. پایتون ما را ملزم میکند که ایمپورتهای from __future__
را در ابتدای ماژول، قبل از هر کد دیگری قرار دهیم.
"""This is the example module.
This module does stuff.
"""
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
import os
import sys
علامتهای نقل قول در رشتهها¶
در پایتون نقل قولهای تکی (') و دوتایی (") هنگام کار با رشتهها تفاوتی ندارند. در این شیوهنامه برای این مورد پیشنهادی ارائه نمیشود. شما یک کدام از این دو را به عنوان قانون کد زنی برای خود انتخاب کنید و با آن ادامه دهید. برای مثال اگر در یک جمله از نقل قول تکی استفاده شده است، شما آن رشته را در یک نقل قول دوتایی قرار دهید تا از بک اسلش در رشتهی خود برای تفکیک علامت نقل قول استفاده نکنید. این عمل باعث افزایش خوانایی کد شما میشود.
هنگام تعریف رشتههای سه گانه، از نقل قولهایی دوتایی به صورت (""") استفاده کنید تا مطابق با قرارداد نوشتن مستندات کد در پپ ۲۵۷ باشد.
فضاهای خالی در عبارات و دستورات¶
موارد روی اعصاب¶
از گذاشتن فضای خالی (فاصله) در موقعیتهای زیر خودداری کنید:
بعد از باز کردن پرانتز، کروشه و براکت؛ همچنین قبل از بستن آنها:
# 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:] ham[lower:upper], ham[lower:upper:], ham[lower::step] ham[lower+offset : upper+offset] ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[lower + offset : upper + offset]
# Wrong: ham[lower + offset:upper + offset] ham[1: 9], ham[1 :9], ham[1:9 :3] ham[lower : : upper] ham[ : upper]
قبل از باز کردن پرانتز در فراخوانی تابع:
# 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
دیگر پیشنهادها¶
از گذاشتن فاصله در انتهای خطوط خودداری کنید. زیرا در بیشتر زمانها این فاصله نامرئی است و میتواند باعث پیچیدگی شود: برای نمونه یک فاصله بعد از بکاسلش در انتهای خط، به عنوان علامت یک خط ادامهدار شناسایی نمیشود. برخی ویرایشگرهای کد از این کار پیشگیری میکنند و بسیاری از پروژهها (مانند خود CPython) به صورت خودکار کاراکتر فاصله در انتهای خطوط را شناسایی و رد میکند.
همیشه قبل و بعد از این عملگرها یک فاصله قرار دهید: مساوی/تخصیص (
=
)، تخصیص غنیشده (مانند+=
،-=
و غیره)، مقایسهها (==
,<
,>
,!=
,<>
,<=
,>=
,in
,not in
,is
,is not
)، عملگرهای بولی منطقی (and
,or
,not
).در صورتی که در یک خط از عملگرهایی با اولویتهای مختلف استفاده میکنید، در چپ و راست عملگر با اولویت کمتر فاصله قرار دهید. به هیچ وجه از بیش از یک فاصله استفاده نکنید و همیشه به همان اندازه در هر دو طرف یک عملگر باینری فضای خالی داشته باشید:
# 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)
انوتیشنهای توابع باید از قوانین عادی برای دونقطه استفاده کنند و در صورت وجود نشانه پیکان
->
همیشه فضای خالی اطراف آن باشد. (برای اطلاعات بیشتر به انوتیشنهای توابع مراجعه کنید.):# Correct: def munge(input: AnyStr): ... def munge() -> PosInt: ...
# Wrong: def munge(input:AnyStr): ... def munge()->PosInt: ...
به هنگام نشان دادن یک آرگومان، یا نشان دادن مقدار پیشفرض پارامتر یک تابع unannotated از فضای خالی اطراف علامت
=
استفاده نکنید:# Correct: def complex(real, imag=0.0): return magic(r=real, i=imag)
# Wrong: def complex(real, imag = 0.0): return magic(r = real, i = imag)
با این حال، هنگام تخصیص مقدار پیشفرض به یک انوتیشن، از فضای خالی اطراف علامت
=
استفاده کنید:# Correct: def munge(sep: AnyStr = None): ... def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
# Wrong: def munge(input: AnyStr=None): ... def munge(input: AnyStr, limit = 1000): ...
نباید از چندین عبارت دستوری در یک خط استفاده کرد:
# Correct: if foo == 'blah': do_blah_thing() do_one() do_two() do_three()
نامناسب:
# Wrong: if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
هرچند گاهی اوقات قرار دادن دستورات تکی if یا for یا while در یک خط مجاز است، اما هیچگاه این کار را برای دستورات چند جملهای انجام ندهید. همچنین از تا کردن این خطوط طولانی نیز خودداری کنید!
نامناسب:
# Wrong: if foo == 'blah': do_blah_thing() for x in lst: total += x while t < 10: t = delay()
قطعاً نه:
# Wrong: if foo == 'blah': do_blah_thing() else: do_non_blah_thing() try: something() finally: cleanup() do_one(); do_two(); do_three(long, argument, list, like, this) if foo == 'blah': one(); two(); three()
زمان استفاده از ویرگول انتهایی¶
ویرگولهای انتهایی معمولا اختیاری هستند، با این تفاوت که هنگام ساختن یک تاپل تک عنصری اجباری هستند. در این صورت برای تمیزی کد، توصیه میشود آن را در پرانتزهای (از نظر فنی زائد) قرار دهید:
# Correct:
FILES = ('setup.cfg',)
# Wrong:
FILES = 'setup.cfg',
استفاده از ویرگول انتهایی اضافه، برای زمانی که از یک سیستم کنترل نسخه استفاده میشود یا زمانی که انتظار میرود لیستی از مقادیر، آرگومانها یا آیتمهای ایمپورت شده در طول زمان زیاد شوند، اغلب مفید است. الگوی استفاده از آن به این صورت است که هر کدام از مقادیر (و غیره) را در هر خط نوشته و به انتهای آن ویرگول انتهایی را اضافه میکنیم و پرانتز، کروشه یا آکولاد بسته را در خط بعدی مینویسیم. با این حال، استفاده از ویرگول انتهایی به همراه پرانتز، کروشه یا آکولاد بسته در یک خط بیمعنی است (به غیر از تاپل تک عضوی مورد بالا):
# Correct:
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
# Wrong:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
اصول نامگذاری¶
اصول نامگذاری در کتابخانه اصلی پایتون کمی شلوغ و نامنظم است، به همین دلیل در این زمینه هیچگاه به یکدست بودن ایدهآل نخواهیم رسید. ماژولها و کدهای جدید پایتون باید با رعایت نکات زیر نوشته شوند، هرچند اگر در کتابخانهای از اصول دیگری پیروی شده بود، حفظ یکدست بودن داخلی کدها، بر اصول پیشنهادی این شیوهنامه اولویت دارد. استانداردهای کنونی و پیشنهادی برای نامگذاری در پایتون به شرح زیر است.
اصل مهم¶
نامهایی که به عنوان بخشی از یک API عمومی به کاربران نمایش داده میشوند، بهتر است با توجه به نوع کارکرد آن بخش از API انتخاب شوند.
توصیفی: سبکهای نامگذاری¶
سبکهای نامگذاری مختلف زیادی وجود دارند و مستقل از آنچه که برای آن استفاده میشوند، کمک میکنند که بتوانیم تشخیص دهیم از چه سبک نامگذاری استفاده میشود.
شیوههای نامگذاری زیر به راحتی قابل تفکیک و تشخیص هستند:
b
(تک حرف کوچک)B
(تک حرف بزرگ)lowercase
lower_case_with_underscores
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords
(یا CapWords نگارش کلمات به هم چسبیده با حرف اول بزرگ ، یا CamelCase (نگارش شتری) -- که این نام گذاری به دلیل ظاهر ناهموار حروف آن است [4]). این نوع نگارش گاهی اوقات به عنوان StudlyCaps نیز شناخته میشود.نکته: هنگام استفاده از کلمات اختصاری در نگارش CapWords، تمام حروفِ کلمه اختصاری با حروف بزرگ نوشته میشود. بنابراین نوشتن HTTPServerError بهتر از HttpServerError است.
mixedCase
(تفاوت آن با نگارش CapitalizedWords این است که اولین حرف آن به صورت کوچک نوشته می شود!)Capitalized_Words_With_Underscores
(زشت!)
همچنین سبک استفاده از یک پیشوند منحصر به فرد کوتاه برای گروهبندی نامهای مرتبط باهم وجود دارد. این سبک خیلی در پایتون استفاده نمیشود، اما برای کامل بودن ذکر شده است. برای مثال، تابع os.stat()
یک تاپل را برمیگرداند که آیتمهای آن معمولاً دارای نامهایی مانند st_mode
، st_size
، st_mtime
و غیره هستند. (این کار برای تأکید بر مطابقت با فیلدهای ساختار سیستم کال POSIX انجام می شود که به برنامهنویسانی که با آن آشنا هستند کمک می کند.)
کتابخانه X11 از یک پیشوند X برای تمام توابع عمومی خود استفاده میکند. در پایتون، این سبک به طور کلی غیرضروری تلقی میشود زیرا پیشوند نام مشخصهها و متدها، نام یک شی است و پیشوند نام توابع نیز، نام یک ماژول.
علاوه بر این، فرمهای ویژه زیر که زیرخطهایی در ابتدا یا انتهای خود دارند، شناخته شده هستند (این فرمها را معمولا میتوان با هر کدام از شیوههای نامگذاری ترکیب کرد):
_single_leading_underscore
: نشانهٔ ضعیف برای «استفاده داخلی». برای نمونه عبارتfrom M import *
اشیائی که اسم آنها با یک زیرخط شروع میشود را ایمپورت نمیکند.single_trailing_underscore_
: طبق قرارداد برای جلوگیری از مغایرت با کلمههای کلیدی پایتون استفاده میشود، برای نمونه:tkinter.Toplevel(master, class_='ClassName')
__double_leading_underscore
: هنگام نامگذاری یک مشخصه کلاس، دستکاری نام (name mangling) را فراخوانی میکند (در داخل کلاس FooBar،__boo
تبدیل به_FooBar__boo
میشود؛ زیر را ببینید).__double_leading_and_trailing_underscore__
: اشیاء و مشخصههای «جادویی» که در فضانامهای تحت کنترل کاربر قرار دارند. برای مثال__init__
,__import__
or__file__
. هرگز چنین اسمهایی را نسازید؛ فقط از آنها طبق مستندات پایتون استفاده کنید.
تجویزی: قواعد نامگذاری¶
نامهای نامناسب¶
هرگز از 'l' (حرف کوچک L)، 'O' (حرف بزرگ o) یا 'I' (حرف بزرگ i) برای نامگذاری متغیرهای تکحرفی استفاده نکنید.
در بعضی فونتها، این حروف از یکدیگر و همچنین از اعداد یک و صفر قابل تفکیک نیستند. برای مثال، به جای استفاده از 'l'، از 'L' استفاده کنید.
سازگاری اَسکی¶
شناسههای استفاده شده در کتابخانه استاندارد باید طبق تعریف بخش سیاستگذاری پپ ۳۱۳۱ با اسکی سازگار باشند.
نام ماژولها و بستهها¶
ماژولها باید نامهایی کوتاه با حروف کوچک داشته باشند. اگر استفاده از زیرخط در نام ماژول میتواند خوانایی را بالاتر ببرد، استفاده از آن مانعی ندارد. بستههای پایتون هم باید نامهایی کوتاه با حروف کوچک داشته باشند و استفاده از زیرخط در نامگذاری بستهها پیشنهاد نمیشود.
وقتی یک ماژول با زبان C یا ++C نوشته شده و یک ماژول همنام با آن در محیط پایتون وجود دارد، به گونهای که ماژول پایتونی، خدمات سطح بالاتر را ارائه میدهد، ماژول نوشته شده با ++C/C باید با یک زیرخط شروع شود (برای مثال _socket
).
نام کلاسها¶
نامگذاری کلاسها باید مطابق با قواعد CapWords باشد. یعنی حرف اول هر کلمه با حروف بزرگ نوشته شود و بین کلمات مختلف هیچگونه فاصله، خط و زیرخط قرار نگیرد.
قواعد نامگذاری توابع ممکن است در مواردی نیز استفاده شود که یک اینترفیس، به عنوان یک شی قابل فراخوانی مستندسازی و استفاده شده باشد.
توجه داشته باشید که برای نامهای داخلی یک قاعده جداگانه وجود دارد: اکثر نامهای داخلی تک کلمهای (یا دو کلمه به هم چسبیده) با قاعده نگارشی CapWords هستند که فقط برای نامهای استثنا و ثابتهای داخلی استفاده میشود.
نام متغیرهای گونه (تایپ)¶
نام گونههای متغیرهای معرفی شده در پپ ۴۸۴ معمولاً باید از قاعدهٔ نگارشی CapWords استفاده کند که نامهای کوتاه را ترجیح میدهد: T
, AnyStr
, Num
. توصیه می شود که پسوندهای _co
یا _contra
را به ترتیب به متغیرهای مورد استفاده برای تعریف رفتار هموردایی یا پادوردایی اضافه کنید:
from typing import TypeVar
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)
نام استثناها¶
از آنجا که استثناها باید کلاس باشند، اصول نامگذاری کلاسها نیز باید روی آنها اعمال شود. با این حال، شما باید از پسوند "Error" برای استثناهایی که تعریف میکنید، استفاده کنید (اگر استثناء شما واقعا یک خطا است).
نام متغیرهای عمومی¶
قواعد تقریباً همان مواردی است که برای توابع اعمال میشود. ( البته این متغیرها باید برای استفاده در یک ماژول در نظر گرفته شده باشند)
ماژولهایی که برای استفاده از طریق from M import *
طراحی شدهاند، باید از مکانیسم __all__
برای جلوگیری از نشان دادن متغیرهای عمومی، یا از قرارداد قدیمیتر قرار دادن زیرخط در اول این متغیرهای عمومی استفاده کنند. (که ممکن است بخواهید این کار را بکنید تا نشان دهید این متغیرهای عمومی "ماژول غیرعمومی" هستند).
نام توابع و متغیرها¶
نام تابع باید از حروف کوچک تشکیل شده باشد و برای بالا بردن خوانایی، کلمات باید با استفاده از زیرخط از هم جدا شوند.
نام متغیرها از همان قواعد نامگذاری توابع پیروی میکند.
استفاده از قاعدهٔ نگارشی mixedCase فقط در زمینههایی مجاز است که قبلاً سبک غالب بوده است (مانند نامهایی که در ماژول threading.py استفاده شده است)، تا سازگاری با نسخههای پیشن را حفظ کند.
آرگومانهای توابع و متدها¶
همیشه از self
به عنوان آرگومان اول متدهای کلاس استفاده کنید.
همیشه از cls
به عنوان آرگومان اول متدهای نوع classmethod
استفاده کنید.
یکسان بودن نام آرگومانهای تابع با کلیدواژههای ذخیره شدهٔ هستهٔ پایتون، باعث به وجود آمدن ناسازگاری میشود. برای رفع این مشکل بهتر است یک زیرخط به انتهای نام آن آرگومان تابع افزوده شود. استفاده از زیرخط بهتر از مخفف کردن نام آرگومان است. برای نمونه، class_
بهتر از clss
است. (هرچند راهحل بهتر آن است که از نامهایی برای آرگومانها استفاده شود که شبیه کلیدواژههای اصلی پایتون نباشند و از اساس این ناسازگاری به وجود نیاید.)
نام متدها و متغیرهای کلاس¶
از اصول نامگذاری تابع پیروی کنید: کلماتی با حروف کوچک که به وسیله زیرخط از هم جدا شدهاند تا خوانایی بالاتر برود.
از زیرخط ابتدای کلمات فقط برای متدهای غیرعمومی و متغیرهای داخلی کلاس استفاده کنید.
برای جلوگیری از تداخل نامها با زیرکلاسها، با استفاده از قوانین دستکاری نامها در پایتون، از دو زیرخط در اول نامها استفاده کنید.
پایتون نام کلاس را به این نامها میچسباند: اگر کلاس Foo مشخصهای به نام __a
دارد، نمیتوان با ``Foo.__a`` به آن دسترسی پیدا کرد. (اگر مصر به دسترسی به آن هستید باید از عبارت Foo._Foo__a
استفده کنید.) به طور کل، استفاده از دو زیرخط اول فقط باید برای جلوگیری از تداخل نامها با نام مشخصههای کلاسهایی که زیرکلاس هستند، استفاده شوند.
نکته: اختلاف نظرهایی در مورد استفاده از __names
وجود دارد (برای اطلاعات بیشتر پایینتر را بخوانید).
ثابتها¶
ثابتها معمولاً در سطح ماژول تعریف میشوند و تماماً با حروف بزرگ نوشته شده و کلمات آنها با زیرخط از هم جدا میشوند. برای نمونه میتوان ثابتهایی با نام MAX_OVERFLOW
و TOTAL
تعریف کرد.
طراحی برای ارثبری¶
همیشه تصمیم بگیرید که کدام متدها و متغیرهای داخلی کلاس (به صورت کلی، مشخصههای کلاس) باید عمومی و کدام یک غیرعمومی باشند. هرگاه شک داشتید، گزینه غیرعمومی را انتخاب کنید؛ زیرا تبدیل کردن یک مشخصه غیرعمومی به عمومی، سادهتر از تبدیل یک مشخصه عمومی به غیرعمومی است.
مشخصههای عمومی آن دسته از مشخصههای کلاس هستند که شما انتظار دارید مصرفکنندگان آن کلاس بتوانند از آنها استفاده کنند و همچنین شما تضمین میدهید که از انجام تغییرات ناسازگار [با نگارشهای پیشین نرمافزار] در آنها جلوگیری کنید. مشخصههای غیرعمومی آنهایی هستند که قرار نیست از بیرون استفاده شوند و شما تعهدی برای عدم حذف یا عدم تغییر آنها ندارید.
اینجا ما از عبارت «خصوصی» استفاده نمیکنیم، به این دلیل که هیچ مشخصهای در پایتون واقعاً خصوصی نیست.
دستهی دیگر مشخصههای کلاس آنهایی هستند که بخشی از "زیرکلاس واسط برنامهنویسی" (subclass API) هستند (اغلب در دیگر زبان های برنامهنویسی به آنها "حفاظت شده" (protected) گفته میشود). برخی از کلاسها به گونهای طراحی شدهاند که از آنها ارثبری شود، یا بخشهایی از رفتارشان گسترش یا تغییر پیدا کند. هنگام طراحی چنین کلاسی، مواظب تصمیمهای صریح دربارهی اینکه کدام مشخصهها عمومی هستند، کدامشان بخشی از زیرکلاس واسط برنامهنویسی و کدامشان فقط قرار است توسط کلاس پایه شما استفاده شود، باشید.
با در نظر داشتن این نکات، راهنمای پایتونی انجام کار به این صورت است:
مشخصههای عمومی نباید با زیرخط (underscore) شروع شوند.
اگر نام مشخصههای عمومی شما با کلیدواژههای از پیش رزرو شده تداخل دارد، یک زیرخط به ابتدای نام مشخصهٔ خود اضافه کنید. این کار نسبت به استفاده از یک مخفف یا یک املای خراب شده ترجیح داده میشود. (با این حال و با وجود این قوانین، املای 'cls' برای هر متغیر یا آرگومانی که میتواند یک کلاس باشد ترجیح داده میشود؛ خصوصاً اگر آرگومان اول یک کلاسمتد باشد.)
نکته ۱: برای کلاسمتدها به توصیههای نامگذاری آرگومانها در بالا دقت کنید.
برای مشخصههای دادهٔ عمومی ساده، بهتر است فقط نام مشخصه، بدون روشهای پیچیدهٔ کمککننده نمایش داده شود. به خاطر داشته باشید، پایتون مسیر بهبود رفتار عملکردی مشخصههای داده را در آینده همیشه هموار نگاه میدارد. برای این کار، از ویژگیهای کلاس استفاده کنید تا پیاده سازی عملکردی را در پشت مشخصهٔ داده، پنهان کنید.
نکته ۱: سعی کنید رفتار توابع بدون عوارض جانبی باشد. اگرچه برخی عوارض جانبی همچون کشینگ معمولاً خوب هستند.
نکته ۲: از ویژگیهای کلاس برای اجرای عملیات سنگین محاسباتی استفاده نکنید. مشخصه باعث می شود که تابع صدا زننده گمان کند که دسترسی (نسبتا) کمهزینه است.
اگر قرار است از کلاس شما ارثبری شود، و مشخصههایی دارد که نمیخواهید زیرکلاسها از آنها استفاده کنند، آنها را با دو زیرخط آغازین و بدون زیرخط پایانی نامگذاری کنید. این کار الگوریتم دستکاری نامها در پایتون را فراخوانی میکند، که نام کلاس را با نام مشخصه کلاس پیوند میزند. این کمک می کند تا اگر زیرکلاسها به طور ناخواسته دارای مشخصههایی با همان نام باشند، از تداخل نام مشخصهها جلوگیری شود.
نکته ۱: توجه داشته باشید که فقط نام کلاس با نام مشخصه کلاس پیوند میخورد، بنابراین اگر نام یک زیرکلاس و مشخصه آن با نام کلاس و مشخصه بالادستی یکی باشد، همچنان دچار تداخل نام میشوید.
نکته ۲: دستکاری نام میتواند کاربردهای خاصی مانند اشکالزدایی و
__getattr__()
را سختتر کند. با این حال، الگوریتم دستکاری نام به خوبی مستند شده است و به راحتی به صورت دستی انجام میشود.نکته ۳: همه از دستکاری نام خوششان نمیآید. سعی کنید تعادل بین «نیاز به جلوگیری از تصادم نام» و «استفاده احتمالی توسط صدازنندههای پیشرفته» را برقرار کنید.
رابطهای عمومی و داخلی¶
هرگونه ضمانت سازگاری با نسخههای پیشین فقط برای اینترفیسهای عمومی اعمال میشود. بر این اساس، مهم است که کاربران بتوانند به وضوح بین اینترفیسهای عمومی و داخلی تمایز قائل شوند.
اینترفیسهای مستند شده، عمومی درنظر گرفته میشوند، مگر اینکه اسناد به صراحت آنها را موقت یا اینترفیسهای داخلی اعلام کند که از ضمانتهای سازگاری با نسخههای پیشین معمول، مستثنی هستند. همه اینترفیسهای غیرمستند باید داخلی فرض شوند.
برای پشتیبانی بهتر از رابطهای داخلی، ماژولها باید به صراحت نامها را در API عمومی خود با استفاده از مشخصه __all__
تعریف کنند. تنظیم __all__
به یک لیست خالی نشان میدهد که ماژول فاقد API عمومی است.
حتی با تنظیم __all__
بصورت صحیح، واسطهای داخلی (بستهها، ماژولها، کلاسها، توابع، مشخصهها و بقیه نامها) هنوز باید همراه با یک زیرخط به صورت پیشوند باشند.
اگر فضانام (بسته، ماژول یا کلاس) دربرگیرندهٔ یک اینترفیس، داخلی در نظر گرفته شود، آن اینترفیس نیز داخلی در نظر گرفته خواهد شد.
نامهای ایمپورتشده همیشه باید به عنوان جزییات پیادهسازی در نظر گرفته شوند. ماژولهای دیگر نباید به دسترسی غیرمستقیم به این نامهای ایمپورتشده متکی باشند، مگر اینکه بخش به صراحت مستندشده از API ماژول باشند، مانند os.path
یا ماژول __init__
در بستهها، که عملکرد زیرماژولها را نشان میدهد.
پیشنهادات برنامهنویسی¶
کد باید به گونهای نوشته شود که با دیگر پیادهسازیهای پایتون (PyPy, Jython, IronPython, Cython, Psyco, و غیره) ناسازگار نباشد.
برای مثال، به پیاده سازی چسباندن درجای رشتهها در CPython برای عباراتی به شکل
a += b
یاa = a + b
تکیه نکنید. این بهینهسازی حتی در CPython هم شکننده است (فقط برای برخی از انواع دادهها کار میکند) و در پیادهسازیهایی که از شمارش ارجاع (refcounting یا Reference counting) استفاده نمیکنند اصلا وجود ندارد. در بخشهای حساس به عملکرد کتابخانه، باید به جای آن از فرم.join()
استفاده شود. این اطمینان حاصل میکند که چسباندن در بین پیادهسازیهای مختلف با پیچیدگی زمانی خطی رخ می دهد.در هنگام بررسی یک متغیر با موارد تکنوعی در پایتون مانند None باید از عبارات
is
یاis not
بجای عملگر مساوی استفاده کنید.همچنین هنگام نوشتن
if x
در حالی که واقعاً منظورتانif x is not None
است، مراقب باشید -- برای نمونه، هنگام آزمایش اینکه آیا متغیر یا آرگومانی که به صورت پیشفرض مقدارش None است، مقدار دیگری دارد یا خیر. مقدار دیگر ممکن است یک نوع داده (مانند یک نوع داده کانتینری) داشته باشد که در یک شرایط بولی ممکن است مقدارش False باشد!از عملگر
is not
به جایnot ... is
استفاده کنید. درحالی که هردو عملکرد مشابهی دارند، فرم اول خوانایی بیشتری نسبت به فرم دوم دارد و ترجیح داده میشود:# Correct: if foo is not None:
# Wrong: if not foo is None:
هنگام پیادهسازی عملکردهای اولویتدار با مقایسههای زیاد، بهتر است هر شش عملکرد (
__eq__
,__ne__
,``__lt__``,__le__
,``__gt__``, ``__ge__ ``) را پیادهسازی کنید به جای اینکه به کدی دیگر فقط برای انجام یک مقایسه خاص تکیه کنید.برای به حداقل رساندن پیچیدگی، دکوراتور
functools.total_ordering()
ابزاری است برای ساختن متدهای مقایسهٔ گم شده.پپ ۲۰۷ نشان میدهد که قوانین بازتاب توسط پایتون در نظر گرفته شده است. بنابراین، مفسر ممکن است
y > x
را باy >= x
،x < y
را باx <= y
، و ممکن است آرگومان هایx == y
وx != y
را تعویض کند. عملکرد توابع `` sort()`` وmin()
برای استفاده از عملگر<
تضمین شده و تابعmax()
از عملگر>
استفاده می کند. با این حال، بهتر است هر شش عملکرد را پیادهسازی کنید تا سردرگمی در زمینههای دیگر ایجاد نشود.همیشه به جای تخصیص مستقیم عبارت لامبدا به یک شناسه، از یک دستور def استفاده کنید:
# Correct: def f(x): return 2*x
# Wrong: f = lambda x: 2*x
شکل اول به این معنی است که نام شی تابع بهدستآمده بهجای کلمه '<lambda>' به طور خاص
f
است. به طور کلی این کار در ردیابیها و نمایش رشتهها مفیدتر است. استفاده از دستور انتساب، تنها مزیتی را که یک عبارت لامبدا میتواند نسبت به یک عبارت def صریح ارائه دهد، حذف میکند (یعنی اینکه میتوان آن را در یک عبارت بزرگتر جا کرد)استثناها را به جای
BaseException
ازException
ارثبری کنید. ارثبری مستقیم ازBaseException
برای استثناهایی است که رخ دادن و گرفتن آنها، تقریباً همیشه کار اشتباهی است.سلسله مراتب استثناها را بر اساس تمایزاتی طراحی کنید که رخ دادن آنها در کد احتمالاً لازم باشد، نه مکانهایی که استثناها در آنجا رخ میدهند. هدف پاسخ برنامهریزی شده به سوال "چه اشتباهی رخ داد؟" است، به جای اینکه فقط بیان کند که "یک مشکل رخ داده است". (برای مثالی از این نکته که برای سلسله مراتب استثناء داخلی گفته شد، پپ ۳۱۵۱ را ببینید)
قراردادهای نامگذاری کلاس در اینجا اعمال میشود، اگرچه اگر استثنا یک خطا باشد، باید پسوند "Error" را به کلاسهای استثنای خود اضافه کنید. استثناهایی که نوع آنها خطا نیست و برای کنترل جریان غیرمحلی یا سایر اشکال سیگنالدهی استفاده میشوند، نیازی به پسوند خاصی ندارند.
از زنجیرهای کردن استثناها به درستی استفاده کنید. «رخ دادن استثناء X به دلیل استثناء Y» باید برای نشان دادن جایگزینی صریح، بدون از دست دادن ردیابی رخداد اصلی استفاده شود.
هنگام جایگزینی عمدی یک استثناء داخلی (با استفاده از فرم "رخ دادن استثناء X از None")، اطمینان حاصل کنید که جزئیات مربوطه، به استثناء جدید منتقل شده است (مانند حفظ نام مشخصه هنگام تبدیل استثناء KeyError به استثناء AttributeError، یا قرار دادن متن استثناء اصلی در پیام استثناء جدید).
هنگام گرفتن استثناها، به جای استفاده از عبارت ساده
except:
، هر زمان که ممکن است، استثناهای مشخصی را ذکر کنید:try: import platform_specific_module except ImportError: platform_specific_module = None
یک عبارت ساده
except:
استثناهای SystemExit و KeyboardInterrupt را میگیرد و بستن برنامه با کلیدهای ترکیبی Control-C را دشوارتر میکند و میتواند مشکلات دیگر را پنهان کند. اگر میخواهید همه استثناهایی را که به خطاهای برنامه سیگنال میدهند، بیابید، ازexcept Exception:
استفاده کنید (except ساده معادلexcept BaseException:
است).یک قانون سرانگشتی خوب این است که استفاده از عبارتهای ساده 'except' را به دو مورد محدود کنید:
اگر کنترلکننده استثنا در حال چاپ کردن یا لاگ کردن ردیابی رخدادها باشد. حداقل کاربر متوجه خواهد شد که یک خطا رخ داده است.
اگر کد نیاز به بازنویسی و تمیزکاری دارد، اما اجازه میدهد تا استثنا با عبارت
raise
بهتر شود. ساختارtry...finally
میتواند راه بهتری برای بکار بردن این مورد باشد.
هنگام گرفتن خطاهای سیستم عامل، سلسله مراتب استثناء واضح معرفی شده در پایتون ۳.۳ را به تشخیص نوع مقادیر
errno
ترجیح دهید.علاوه بر این، برای تمام عبارات try/except در بند
try
سعی کنید کمترین میزان کد را بنویسید. این کار از پوشاندن سایر اشکالات در برنامه جلوگیری میکند:# Correct: try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
# Wrong: try: # Too broad! return handle_value(collection[key]) except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
وقتی منبعی متعلق به بخش خاصی از کد است، از عبارت
with
استفاده کنید تا مطمئن شوید که پس از استفاده، به سرعت و با اطمینان پاکسازی میشود. عبارت try/finally نیز قابل قبول است.مدیران زمینه (Context managers) باید از طریق توابع یا متدهای جداگانه، هر زمان که کاری غیر از گرفتن و آزادسازی منابع انجام میدهند، فراخوانی شوند:
# Correct: with conn.begin_transaction(): do_stuff_in_transaction(conn)
# Wrong: with conn: do_stuff_in_transaction(conn)
مثال دوم هیچ اطلاعاتی ارائه نمیدهد که نشان دهد متدهای
__enter__
و__exit__
کاری غیر از بستن اتصال پس از تراکنش انجام میدهند. صریح بودن در این مورد مهم است.درمورد دستورات return ثابت قدم باشید. یا تمام دستورات return در یک تابع باید یک عبارت را برگردانند یا هیچ کدام نباید. اگر هر دستور return یک عبارت را برمیگرداند، هر دستور return که در آن هیچ مقداری برگردانده نشده است باید به صراحت آن را به عنوان
return None
بیان کند، و یک دستور return صریح باید در انتهای تابع وجود داشته باشد (اگر قابل دسترسی باشد):# Correct: def foo(x): if x >= 0: return math.sqrt(x) else: return None def bar(x): if x < 0: return None return math.sqrt(x)
# Wrong: def foo(x): if x >= 0: return math.sqrt(x) def bar(x): if x < 0: return return math.sqrt(x)
به منظور بررسی وجود پیشوند یا پسوند در یک رشته، بجای برش آن رشته، از متدهای
''.startswith()
و''.endswith()
استفاده کنید.متدهای
startswith()
وendswith()
تمیزتر و کمخطاتر هستند:# Correct: if foo.startswith('bar'):
# Wrong: if foo[:3] == 'bar':
در هنگام مقایسهی اشیاء بجای مقایسهٔ نوع آنها به صورت مستقیم، همیشه از متد
isinstance()
استفاده کنید:# Correct: if isinstance(obj, int):
# Wrong: if type(obj) is type(1):
برای نوع دادههایی که دارای ترتیب هستند (رشتهها، لیستها و تاپلها)، به این واقعیت اتکا کنید که مقادیر خالی آنها غلط هستند:
# Correct: if not seq: if seq:
# Wrong: if len(seq): if not len(seq):
حروف رشتهای که متکی به فضای خالی انتهایی قابل توجهی هستند را ننویسید. چنین فضای خالی انتهایی از نظر بصری قابل تشخیص نیست و برخی از ویرایشگرها (یا اخیراً، reindent.py) آنها را حذف میکنند.
مقادیر بولی را با استفاده از
==
با True و False مقایسه نکنید:# Correct: if greeting:
# Wrong: if greeting == True:
بدترین:
# Wrong: if greeting is True:
استفاده از عبارات کنترل جریان
return
/break
/continue
در سلسله نهایی ساختارtry...finally
، جایی که دستور کنترل جریان به خارج از سلسله نهایی میپرد، کارایی ندارد. دلیل این امر این است که چنین عباراتی به طور ضمنی هر استثنا فعالی را که در سلسله نهایی رخ میدهد، لغو میکنند:# Wrong: def foo(): try: 1 / 0 finally: return 42
انوتیشنهای توابع¶
با پذیرش پپ ۴۸۴ شیوه نگارش انوتیشن توابع تغییر کرد.
انوتیشنهای توابع باید مطابق با شیوهنامه پپ ۴۸۴ باشند. (در بخش قبل، برخی اصول پیشنهادی برای انوتیشنها آورده شده).
استفادههای آزمایشی از انوتیشنها که در نسخههای پیشین پپ ۸ گفته شده بود از این به بعد پیشنهاد نمیشود.
با این حال اکنون، آزمایش قوانین پپ ۴۸۴ خارج از کتابخانه استاندارد نیز پیشنهاد میشوند. برای نمونه، علامتگذاری یک کتابخانه یا برنامه شخص ثالث بزرگ با سبک انوتیشنهای پپ ۴۸۴، بررسی آسان بودن افزودن آن انوتیشنها، و مشاهده اینکه آیا وجود آنها درک کد را افزایش می دهد یا خیر.
کتابخانه استاندارد پایتون باید در پذیرش چنین انوتیشنهایی محافظهکار باشد، اما استفاده از آنها برای کدهای جدید و بازسازیهای بزرگ کدها مجاز است.
برای کدهایی که میخواهند استفادهٔ دیگری از انوتیشنهای تابع داشته باشند، پیشنهاد میشود از کامنتهایی با فرم زیر استفاده شود:
# type: ignore
در خطوط اولیهٔ فایل؛ این خط به چککنندههای تایپها پیغام میدهد که انوتیشنها را در این فایل نادیده بگیرند. (برای دیدن روشهای دیگر غیرفعال کردن انوتیشنها به پپ ۴۸۴ مراجعه کنید).
مانند لینترها، چککنندههای نوع داده اختیاری هستند و ابزارهایی جدا به حساب میآیند. مفسرهای پایتون به طور پیشفرض نباید هیچ پیامی را به دلیل بررسی نوع داده نشان دهند و نباید رفتار خود را بر اساس انوتیشنها تغییر دهند.
کاربرانی که نمیخواهند از چککنندههای نوع داده استفاده کنند، میتوانند آنها را نادیده بگیرند. با این حال، انتظار میرود که کاربران بستههای کتابخانه شخص ثالث ممکن است بخواهند بررسیهای نوع داده را روی آن بستهها انجام دهند. برای این منظور پپ ۴۸۴ استفاده از فایلهای خرد (stub files) را توصیه میکند: فایلهای .pyi که توسط چککننده نوع داده در اولویت فایلهای .py مربوطه خوانده میشوند. فایلهای خرد را میتوان با کتابخانه، یا به صورت جداگانه (با اجازه نویسنده کتابخانه) از طریق مخزن typeshed [5] توزیع کرد.
انوتیشنهای متغیرها¶
پپ ۲۵۶ انوتیشنهای متغیر را معرفی کرد. شیوهنامه پیشنهادی شامل قانونهای مشابه با انوتیشن توابع است که بالاتر توضیح داده شد:
انوتیشن برای متغیرهای سطح ماژول، کلاس و متغیرهای داخلی کلاس و متغیرهای محلی باید یک فاصله بعد از دونقطه داشته باشند.
هیچ فاصلهای نباید پیش از دونقطه قرار بگیرد.
هنگام تخصیص مقدار به یک متغیر، باید قبل و بعد از علامت
=
یک فاصله قرار بگیرد:# Correct: code: int class Point: coords: Tuple[int, int] label: str = '<unknown>'
# Wrong: code:int # No space after colon code : int # Space before colon class Test: result: int=0 # No spaces around equality sign
هرچند پپ ۵۲۶ برای پایتون ۳,۶ پذیرفته شده است، ساختار انوتیشن متغیرها، ساختار ترجیح داده شده برای فایلهای stub در تمام نگارشهای پایتون است (برای جزئیات بیشتر پپ ۴۸۴ را ببینید).
پانویسها
منابع¶
شیوهنامه نگارش گنو میلمن https://bit.ly/3wZ7J5I
دانلد ناتس The TeXBook، صفحات ۱۹۵ و ۱۹۶.
مخزن تایپشد https://github.com/python/typeshed
کپیرایت¶
این شیوهنامه به صورت آزاد در دسترس همگان قرار گرفته است.
ترجمه فارسی¶
مشارکتکنندگان¶
مشارکت در ترجمه¶
برای مشارکت در ترجمه این متن به مخزن گیتهاب پروژه مراجعه کنید.
سایت پپ ۸ فارسی، پروژهای از حنیف بیرگانی.
کامنتها¶
کامنتهایی که با کد در تناقض هستند بدتر از کامنتهایی هستند که هرگز نوشته نشدند. همیشه کامنتها را همگام با تغییرات کدها بهروز نگه دارید!
کامنتها باید جملات کامل باشند. کلمهٔ اول باید با حروف بزرگ آغاز شود، مگر اینکه کلمهٔ اول به یک شناسه (نام یک متغیر، تابع، ماژول و...) اشاره کرده باشد که با حروف کوچک آغاز شود.
کامنتهای بلوکی معمولا شامل یک پاراگراف یا بیشتر هستند. پاراگرافها باید از جملات کامل تشکیل شده باشند و هر جمله باید با علامت نقطه به پایان رسیده باشد.
در کامنتهای چندجملهای، شما باید پس از نقطهٔ آخر هر جمله از دو فاصله استفاده کنید؛ به استثنای آخرین جمله.
اطمینان حاصل کنید که کامنتهای شما واضح و به آسانی قابل فهم هستند.
برنامهنویسانِ پایتون غیرانگلیسیزبان: لطفاً کامنتهای خود را به زبان انگلیسی بنویسید، مگر اینکه ۱۲۰٪ مطمئن باشید که کدهای شما را هرگز هیچ برنامهنویس غیرهمزبان شما نخواهد خواند.
کامنتهای بلوکی¶
کامنتهای بلوکی معمولا به کدهایی که بعد از آنها نوشته میشود اشاره دارند. تورفتگی این کامنتها باید به اندازه تورفتگی کدهای مربوطه باشد. هر خط از کامنتهای بلوکی باید با علامت
#
و یک فاصله پس از آن آغاز شود (به جز در مواردی که درون بلوک کامنتها هم نیاز به تورفتگی وجود داشته باشد).پاراگرافهای درون یک بلوک کامنت، توسط خطی که با یک علامت
#
شروع میشود از هم جدا میشوند.کامنتهای درونخطی¶
تا حد امکان از کامنتهای درونخطی استفاده نکنید.
کامنت درونخطی در واقع کامنتی است که در همان خط کد نوشته میشود. کامنتهای درونخطی باید حداقل با دو فاصله از کد نوشته شوند. آنها باید با یک علامت # و یک فاصله بعد از آن آغاز شوند.
کامنتهای درونخطی در صورتی که توضیح واضحات باشند، کاملا غیرضروری و مزاحم هستند. این کار را انجام ندهید:
اما گاهی وقتها میتواند مفید باشد:
رشتههای مستندات¶
اصول کامل برای نوشتن مستندات خوب در پپ ۲۵۷ شرح داده شدهاند.
برای تمام ماژولها، توابع، کلاسها و متدهای خود که به صورت عمومی تعریف کردهاید مستندات مربوط به آنها را بنویسید. نوشتن این مستندات برای سایر متدهایی که به صورت عمومی تعریف نشدهاند الزامی نیست؛ اما باید برای این متدها نیز یک کامنت بنویسید تا عمکلرد آن را شرح دهد. این کامنت باید در پایین خط
def
(اولین خط درون متد شما) نوشته شود.پپ ۲۵۷ اصول مستندنویسی را به خوبی توضیح میدهد. نکته مهم این است که عبارت
\"\"\"
که برای پایان دادن به مستندات استفاده میشود باید در یک خط جداگانه قرار گیرد.برای مستندات یک خطی، لطفاً
"""
پایانی را در همان خط قرار دهید:"""Return an ex-parrot."""