درس ۰۶: سینتکس یا دستور نحو زبان پایتون¶

Photo by Tim J
در این درس به معرفی اجزای پایه در برنامهنویسی پایتون پرداخته شده و اینکه چه هستند، چه گرامری دارند، چه کاری انجام میدهند یا... مورد بررسی قرار گرفته است. همچنین در موارد بسیاری نکتههایی از شیوه استاندارد پایتوننویسی که توسط سند PEP 8 به برنامهنویسان پایتون پیشنهاد میشود نیز آورده شده است؛ رعایت این اصول به یکدستی کدهای جامعه پایتون کمک میکند.
سینتکس (Syntax یا دستور نحو) مجموعهای از قواعد است که چگونگی برنامهنویسی به یک زبان مشخص را تعریف میکند؛ برای نمونه اینکه یک متن چطور نوشته شود که توسط مفسر پایتون به عنوان توضیح در نظر گرفته شود یا یک شی رشته، به رعایت سینتکس تعریف شده در پایتون بستگی دارد و چنانچه مفسر نتواند متن را با هیچ قاعده تعریف شدهای مطابقت دهد یک استثنا گزارش خواهد شد. سینتکس پایتون تنها محدود به این درس نیست و موارد بسیار دیگری به مانند چگونگی تعریف اشیا گوناگون را در طی درسهای آتی مشاهده خواهید کرد.
✔ سطح: مقدماتی
سرفصلها
سطرها¶
مفسر پایتون و همچنین کاربر، کدهای درون هر ماژول را در قالب تعدادی سطر میبینند؛ سطرهای فیزیکی (Physical Lines) و منطقی (Logical Lines). سطرهای فیزیکی در واقع همان سطرهایی است که توسط ویرایشگرهای متن شمارهگذاری میشوند و به راحتی توسط کاربر قابل تشخیص هستند ولی سطرهای منطقی برداشت مفسر از اجزای برنامه است؛ هر سطر منطقی بیانگر یک دستور (Statement) پایتون است. برای نمونه دستور print
در نسخه 2x پایتون را در نظر بگیرید:
1 2 | msg = "Welcome!"
print msg
|
در نمونه کد بالا: سطر یکم، یک دستور انتساب (Assign) را نشان میدهد؛ این دستور مقدار سمت راست خودش را به متغیر msg
نسبت میدهد. کم و بیش با دستور سطر دوم نیز آشنا هستید این دستور مقدار مربوط به متغیر دریافتی را بر روی خروجی نمایش میدهد. در اینجا دو دستور یعنی دو سطر منطقی وجود دارد که هر یک در قالب یک سطر فیزیکی پیادهسازی شده است.
هر چند که پیشنهاد میشود همیشه هر سطر فیزیکی تنها شامل یک سطر منطقی باشد ولی یک سطر فیزیکی را میتوان شامل چند سطر منطقی نیز در نظر گرفت:
1 | msg = "Welcome!"; print msg
|
در این حالت میبایست سطرهای منطقی (یا همان دستورها)، توسط کاراکتر ;
(Semicolon) از یکدیگر جدا گردند.
گاهی نیز بهتر است برای خوانایی بیشتر، یک سطر منطقی را در قالب چند سطر فیزیکی پیادهسازی نماییم:
1 2 3 | msg = "Python Programming \
Language." # This is a message.
print msg
|
در نمونه کد بالا: دو سطر فیزیکی نخست از دید مفسر به شکل تنها یک سطر منطقی دیده میشود. در پایتون برای شکستن یک دستور در چند سطر فیزیکی از کاراکتر \
(Backslash) استفاده میگردد. البته توجه داشته باشید که از \
نمیتوان برای شکستن سطر توضیح (Comment) استفاده کرد و همچنین نمیتوان پس از آن هیچ توضیحی درج کرد.
نکته
[PEP 8]: طول هر سطر فیزیکی نباید از ۷۹ کاراکتر بیشتر شود. برای متنهای طولانی نیز مانند توضیح (Comment) و Docstring طول هر سطر فیزیکی باید حداکثر ۷۲ کاراکتر در نظر گرفته شود.
برای خوانایی بیشتر بهتر است دستورهای طولانی شکسته شوند. دستورهایی که شامل { }
، [ ]
و ( )
هستند را میتوان بدون استفاده از \
شکست و در قالب چند سطر فیزیکی نوشت:
1 2 3 4 | month_names = ['Januari', 'Februari', 'Maart', # These are the
'April', 'Mei', 'Juni', # Dutch names
'Juli', 'Augustus', 'September', # for the months
'Oktober', 'November', 'December'] # of the year
|
که در این حالت برخلاف استفاده از \
میتوان پس از شکستن سطرها، توضیح نیز اضافه کرد.
«سطرهای خالی» (Blank lines): سطری که تنها حاوی فضای خالی (Spaceها یا Tabها) باشد، توسط مفسر نادیده گرفته میشود و به بایتکد ترجمه نمیگردد. از این سطرها میتوان برای خوانایی بیشتر کدها بهره گرفت - مانند سطر سوم در نمونه کد پایین:
1 2 3 4 | def power(a, b):
return a ** b
print power(2, 3)
|
مستندسازی¶
هر چند اساس طراحی زبان پایتون بر خوانایی بالای کد است ولی «مستندسازی» (Documentation) برنامه یعنی استفاده از امکاناتی همچون ارایه توضیح در کدها میتواند به درک و خوانایی هر چه بهتر کدهای برنامه برای مراجعات آینده برنامهنویس و افراد دیگری که میخواهند بر روی توسعه آن فعال باشند یا از آن استفاده کنند نیز بسیار مفید خواهد بود. در این بخش به بررسی دو امکان درج Comment و Docstring برای مستندسازی برنامه میپردازیم.
توضیح¶
یک «توضیح» (Comment) در زبان پایتون توسط کاراکتر #
آغاز میشود و با پایان سطر فیزیکی هم پایان میپذیرد. توضیحها نیز مانند سطرهای خالی توسط مفسر نادیده گرفته شده و به بایتکد ترجمه نمیشوند.
هدف از نوشتن توضیح در میان کدها، شرح منطق یک تکه کد است و اینکه کد مورد نظر چرا نوشته شده و چه کاری انجام میدهد. گاهی نیز از ویژگی توضیح (اینکه توسط مفسر نادیده گرفته میشود) برای غیرفعال کردن کدها بهره گرفته میشود. توضیح نقش زیادی در خوانایی کدها دارد و استفاده مرتب از آن پیشنهاد میشود.
توضیح در پایتون تنها به شکل تک سطری تعریف شده است و برای درج توضیحهایی با بیش از یک سطر فیزیکی باید توجه داشت که هر سطر به صورت جداگانه میبایست با #
آغاز شود.
نکته
[PEP 8]: متن توضیح با یک فضای خالی (Space) بعد از #
آغاز شود. در توضیحهای چند سطری برای جداسازی پاراگرافها از یک سطر توضیح بدون متن (سطری خالی که با #
آغاز میشود) استفاده شود. هنگام درج توضیح در همان سطرهای دستور، توضیح حداقل به اندازه دو فضای خالی از انتهای دستور فاصله داده شود.
1 2 3 4 5 6 7 8 9 | # A comment, this is so you can read your program later.
# Anything after the # is ignored by python.
print "I could have code like this." # and the comment after is ignored
# You can also use a comment to "disable" or comment out a piece of code:
# print "This won't run."
print "This will run."
|
Docstring¶
در کنار «توضیح»؛ ”Docstring“ نیز امکان دیگری در پایتون برای ارایه توضیح بیشتر درباره کدهای برنامه است. متن Docstring توسط سه علامت نقل قول ("""
یا '''
) شروع و پایان مییابد [PEP 257] و معمولا از آن به عنوان نخستین دستور در ماژول، کلاس، تابع و متد استفاده میشود که در این شرایط Docstring توسط مفسر نادیده گرفته نمیشود و در زمان اجرا نیز با استفاده از صفت __doc__
قابل دستیابی است:
1 2 3 4 5 6 7 | def complex(real=0.0, imag=0.0):
"""Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
"""
|
>>> complex.__doc__
'Form a complex number.\n\n Keyword arguments:\n real -- the real part (default 0.0)\n imag -- the imaginary part (default 0.0)\n '
>>> print(complex.__doc__)
Form a complex number.
Keyword arguments:
real -- the real part (default 0.0)
imag -- the imaginary part (default 0.0)
>>>
ملاحظه
n\
بیانگر پایان سطر جاری و رفتن به سطر بعدی است - برای مشاهده درست این چنین متنهایی که حاوی n\
هستند میبایست از print
استفاده نمایید.
مخاطب متن «توضیح» موجود در کد، کاربرانی میباشند که آن کد را توسعه میدهند در حالی که مخاطب اصلی Docstringها کاربرانی است که از کد مربوط به آن استفاده میکنند بنابراین Docstring باید به توضیح چگونگی استفاده از کد (به صورت خاص: ماژول، تابع، کلاس و متد) بپردازد.
Docstring باید به عنوان دستور نخست درج گردد و این نکته برای یک ماژول در صورت وجود سطرهای اجرای مفسر و تعیین کدگذاری به صورت پایین در نظر گرفته میشود:
#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
Module docstring.
"""
import [...]
[...]
بستهها (Packages) نیز میتوانند Docstring داشته باشند؛ برای این منظور Docstring باید درون ماژول init__.py__
نوشته شود.
نکته
Docstringها در هر جای دیگری از کدهای برنامه نیز به عنوان جایگزینی برای توضیحهای چند سطری قابل استفاده هستند که در این حالت مفسر آنها نادیده گرفته و دیگر قابل دستیابی نیستند.
تورفتگی¶
بلاکبندی در زبان پایتون توسط «تورفتگی» (Indentation) سطرها مشخص میگردد؛ این عمل در زبانهایی مانند C توسط آکولاد { }
انجام میشود. تورفتگی در واقع عبارت است از میزان فضای خالی (Spaceها و Tabها) هر دستور از ابتدای سطر فیزیکی خود. نکته مهم این است که تمام دستورهای موجود در یک بلاک میبایست به یک میزان فاصله نسبت به سرآیند خود تورفتگی داشته باشند:
1 2 3 4 5 6 | // C
if (x > y) {
x = 1;
y = 2;
}
|
1 2 3 4 5 | # Python
if x > y:
x = 1
y = 2
|
در تصویر پایین به شیوه تورفتگی بلاکها نسبت به سرآیند خود توجه نمایید:

نکته
[PEP 8]: در ایجاد تورفتگی استفاده از کلید Space نسبت به کلید Tab ترجیح داده میشود - برای هر مرتبه تورفتگی از چهار کلید Space استفاده نمایید.
روش رایج ایجاد تورفتگی استفاده از کلید Space است و سعی کنید هرگز به صورت ترکیبی از کلیدهای Sapce و Tab استفاده نکنید هر چند که در نسخه 3x پایتون امکان استفاده ترکیبی از این دو کلید وجود ندارد! اگر مایل به استفاده از کلید Tab هستید باید به صورت یکدست تمام تورفتگیهای برنامه خود را فقط با استفاده از آن ایجاد نمایید.
اجباری به تورفتگی آن بخشی از دستورها که به سطرهای فیزیکی دیگر شکسته شدهاند وجود ندارد اما بهتر است برای خوانایی بالاتر، این بخشها را با کمی تورفتگی بیشتر نسبت به دستورهای بدنه بلاک جاری نوشته شوند:
1 2 3 4 | def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
|
در دستورهایی به مانند پایین نیز ایجاد تراز آرگومانها هم حالت مناسبی است:
1 2 | foo = long_function_name(var_one, var_two,
var_three, var_four)
|
دستور¶
«دستور» (Statement) واحدی از کد است که شامل کلمههای کلیدی بوده، اجرا میگردد و کاری را به انجام میرساند. در پایتون دو نوع دستور وجود دارد:
دستورهای ساده (Simple Statements): دستورهایی هستند که تنها در یک سطر منطقی پیادهسازی میشوند. مانند دستور import
، دستور pass
، دستور انتساب، فراخوانی تابع و...
دستورهای مرکب (Compound Statements): گروهی از دستورها هستند که میتوانند یک بخشی (مانند: دستور def
- تعریف تابع) یا چند بخشی (مانند: دستور شرط if
/ elif
/ else
) باشند؛ هر بخش (Clause) نیز شامل یک سرآیند (Header) و یک بدنه (Suite) است. هر سرآیند با یک کلمه کلیدی آغاز میشود و با یک :
(Colon) نیز پایان میپذیرد. بدنه پس از سرآیند و با رعایت سطح تورفتگی بیشتر نسبت به آن نوشته میشود:
1 2 3 4 5 6 7 8 9 10 11 12 | def binary_search(seq, key):
lo = 0
hi = len(seq) - 1
while hi >= lo:
mid = lo + (hi - lo) // 2
if seq[mid] < key:
lo = mid + 1
elif seq[mid] > key:
hi = mid - 1
else:
return mid
return False
|
شناسه¶
«شناسه» (Identifier) نامی است نمادین که به دلخواه کاربر تعیین و از آن برای شناسایی (identify) متغیرها، توابع، کلاسها، ماژولها یا دیگر اشیا پایتون از یکدیگر استفاده میشود. انتخاب شناسه در پایتون نکاتی دارد که میبایست از سوی کاربر در نظر گرفته شود:
- تنها با یکی از حروف بزرگ یا کوچک الفبا انگلیسی (
A..Z
یاa..z
) یا کاراکتر_
(Underscore) شروع شود. - در ادامه میتواند هیچ یا چند حرف الفبا انگلیسی (کوچک و بزرگ)، کاراکتر
_
و عدد (9..0
) - با هر ترتیبی - آورده شود. - هیچ محدودیتی در طول شناسه وجود ندارد و میتواند از یک تا هر تعداد کاراکتر باشد.
با یک نگاه حرفهایتر، ساختار لغوی (Lexical) شناسه به شکل پایین بیان میشود [اسناد پایتون]:
identifier ::= (letter|"_") (letter | digit | "_")*
letter ::= lowercase | uppercase
lowercase ::= "a"..."z"
uppercase ::= "A"..."Z"
digit ::= "0"..."9"
ملاحظه
در تعاریف regex: از پرانتز ( )
برای گروهبندی استفاده میشود. نماد |
به معنی یا میباشد و از آن برای جداسازی دو عبارت متفاوت استفاده میشود. نماد *
به معنی صفر بار تکرار یا بیشتر میباشد. [درسی جداگانه به regex اختصاص خواهد یافت]
نکته
- استفاده از کاراکترهای خاص به مانند
.
،!
،@
،#
،$
،%
و... مجاز نمیباشد. - استفاده از «فاصله» (Space) مجاز نمیباشد.
- استفاده از «خط تیره» (Hyphen) یعنی کاراکتر
-
برای جداسازی کلمهها در نام ماژول مجاز است ولی پیشنهاد نمیشود.
برای نمونه - چند شناسه درست:
a _p __var MyClass get_length getLength var2 var_2 S01E16
برای نمونه - چند شناسه نادرست:
[email protected] get.length 2_var 6 $var 4pi
نکته
همانطور که از درس یکم میدانیم، پایتون یک زبان حساس به حرف (Case Sensitive) است و مفسر آن بین حروف کوچک (Lowercase) و بزرگ (Uppercase) به مانند a و A تمایز میگذارد.
برای نمونه، تمامی شناسههای CaR ،cAR ،CAr ،caR ،cAr ،Car ،car و CAR متفاوت با یکدیگر ارزیابی میشوند.
در پایتون از تکنیکی به نام Name Mangling استفاده میشود. توسط این تکنیک و تنها با شیوه انتخاب شناسهها، نقشی خاص به آنها داده میشود:
- شناسه خصوصی (Private) ماژول: اگر شناسهای با یک کاراکتر
_
آغاز شود (و نه پایان پذیرد) توسط مفسر پایتون در این نقش ارزیابی میگردد. مانند:name_
(و نه:_name_
یا_name
) - شناسه خصوصی کلاس: اگر شناسهای با دو کاراکتر
_
آغاز شود (و نه پایان پذیرد) توسط مفسر پایتون در این نقش ارزیابی میگردد. مانند:name__
(و نه:__name__
یا__name
)
جدا از این مورد، در پایتون صفتها (Attributes) و متدهای خاصی وجود دارد که از پیش تعریف گشتهاند و برای مفسر مفهوم مشخصی دارند. شناسه این صفتها و متدها با دو کاراکتر _
آغاز میشود و همینطور پایان میپذیرد؛ درست به مانند صفتهای __class__
و __doc__
که پیش از این استفاده کردیم.
بنابراین به هنگام استفاده از کاراکتر _
در شناسه (به خصوص در ابتدای آن) باید آگاهی کافی داشته باشیم. [به موارد اشاره شده در آینده پرداخته خواهد شد.]
نکته
[PEP 8]: شیوه استاندارد انتخاب شناسه برای کلاس، تابع، متد و متغیر به صورت پایین است:
- کلاسها به شیوه PascalCase - یعنی تنها حرف نخست هر کلمه بزرگ باشد و کلمهها بدون فاصله کنار هم قرار بگیرند - نامگذاری شوند. مانند: AnimalClass ،Animal.
- نام انتخابی برای یک تابع و متد نیز باید تنها شامل حروف کوچک باشد و برای جداسازی کلمهها از
_
استفاده شود. مانند: bubble_sort ،binary_search و... البته میتوان از شیوه camelCase (همانند PascalCase با این تفاوت که حرف نخست کلمه یکم هم میبایست حرف کوچک باشد) نیز استفاده نماییم. مانند: bubbleSort ،binarySearch و... - نام متغیرها تنها باید شامل حروف کوچک باشد که کلمههای آن توسط
_
از یکدیگر جدا شدهاند. مانند: body_color ،color و...
برای شناسههای تک حرفی توجه داشته باشید که از انتخاب حروف l (اِل کوچک) و I (آی بزرگ) اجتناب کنید زیرا این دو حرف در برخی فونتها شبیه هم هستند و البته همینطور حرف O (اُ بزرگ) که میتواند شبیه به صفر باشد.
کلمههای کلیدی¶
نکته پایانی در مورد شناسهها این است که: نمیتوان یک شناسه را برابر با یکی از «کلمههای کلیدی» (keywords) پایتون انتخاب کرد. کلمههای کلیدی در واقع شناسههایی هستند که از پیش برای مفسر پایتون تعریف شدهاند و معنای مشخصی برای آن دارند. فهرست این کلمههای در پایتون به صورت پایین است:
>>> # Python 3.x
>>> help("keywords")
Here is a list of the Python keywords. Enter any keyword to get more help.
False def if raise
None del import return
True elif in try
and else is while
as except lambda with
assert finally nonlocal yield
break for not
class from or
continue global pass
>>> # Python 2.x
>>> help("keywords")
Here is a list of the Python keywords. Enter any keyword to get more help.
and elif if print
as else import raise
assert except in return
break exec is try
class finally lambda while
continue for not with
def from or yield
del global pass
در کتابخانه استاندارد پایتون ماژولی به نام keyword
وجود دارد [اسناد پایتون]:
>>> # Python 3.x
>>> import keyword
>>> keyword.iskeyword(a)
False
>>> keyword.iskeyword("def")
True
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
ملاحظه
تابع ()iskeyword
بررسی میکند که آیا آرگومان دریافتی یکی از کلمههای کلیدی میباشد یا نه؛ در صورت تایید مقدار True
را باز میگرداند. kwlist
نیز در واقع یک شی لیست حاوی تمام کلمههای کلیدی است.
شاید کنجکاو باشید تعداد کلمههای کلیدی پایتون را بدانید؛ برای این منظور نیازی به شمارش دستی نیست!:
>>> # Python 3.x
>>> import keyword
>>> len(keyword.kwlist)
33
>>> # Python 2.x
>>> import keyword
>>> len(keyword.kwlist)
31
ملاحظه
تابع ()len
تعداد اعضای یک شی را باز میگرداند [اسناد پایتون].
برخی نکات:
- در پایتون 3x: تنها کلمههای کلیدی
True
،False
وNone
با حرف بزرگ آغاز میشوند. - در پایتون 2x: کلمههای کلیدی
True
،False
وNone
تعریف نشده است. - در پایتون 3x: از آنجا که print به یک تابع تبدیل گشته، کلمه کلیدی
print
تعریف نشده است.
نکته
[PEP 8]: چنانچه میخواهید شناسهای مشابه با یکی از کلمههای کلیدی انتخاب نمایید؛ میتوانید این کار را با استفاده از یک _
در انتهای کلمه مورد نظر به انجام برسانید. مانند: _def
متغیر¶
یک «متغیر» (Variable) در بیشتر زبانهای برنامهنویسی به مانند C بیانگر محلی در حافظه میباشد که مقداری در آن قرار گرفته است. برای نمونه سه دستور پایین را در نظر بگیرید:
int a = 1;
a = 2;
int b = a;
در نمونه کد بالا: دستور ;int a = 1
بیان میکند که محلی از حافظه به نام a
برای نگهداری اعداد صحیح (integers) در نظر گرفته شود و مقدار 1
در آن قرار بگیرد؛ از این پس متغیر a
معرف این نقطه از حافظه میباشد (درست به مانند یک جعبه) که اکنون حاوی مقدار 1
است (شکل پایین - یک). در ادامه دستور ;a = 2
موجب میشود مقدار پیشین متغیر a
حذف (از جعبه خارج) و مقدار جدید یعنی 2
در آن قرار داده شود (شکل پایین - دو). توجه داشته باشید که در این دسته زبانها، نوع (type) توسط متغیر تعیین میگردد و تلاش برای قرار دادن نوع داده دیگری به غیر از int
در متغیر a
(به مانند 3.7
یا "string"
) موجب بروز خطا در زمان کامپایل میگردد. دستور سوم: ;int b = a
در ابتدا موجب ایجاد یک محل جدید در حافظه با نام b
و از نوع همان اعداد صحیح میشود و سپس مقدار درون متغیر a
را درون آن کپی میکند؛ اکنون دو محل برای نگهداری نوع داده int
در حافظه موجود است که هر دو حاوی مقدار 2
میباشند (شکل پایین - سه).

ولی در پایتون:
یک متغیر چیزی نیست جز یک نام که به یک شی مشخص در حافظه ارجاع (یا اشاره) دارد. تعریف متغیر در پایتون بسیار ساده است و تنها با انتساب (Assign) شی به یک نام ایجاد میگردد. نمادِ =
، عملگر (Operator) انتساب در پایتون است. در تعریف متغیر پایتون برخلاف آنچه در زبان C مشاهده کردیم ;int a
، نیازی به تعیین نوع برای آن نیست چرا که نوع (type) از روی شی تعیین میگردد و یک متغیر در طول زمان اجرا میتواند به شیهایی از انواع متفاوت ارجاع داشته باشد. برای نمونه سه دستور پایین را در نظر بگیرید:
a = 1
a = 2
b = a
مفسر با رسیدن به دستور a = 1
، سه گام پایین را انجام میدهد:
- یک شی از نوع اعداد صحیح و مقدار
1
را در جایی از حافظه ایجاد میکند. چرا اعداد صحیح؟ نوع توسط شی تعیین میگردد و1
عددی است صحیح!. - متغیرِ (یا همان نامِ)
a
را در جایی دیگر از حافظه ایجاد میکند (البته در صورتی که قبلا ایجاد نشده باشد). - یک پیوند از متغیر
a
به شی1
برقرار میکند. به این پیوند «ارجاع» (Reference) گفته میشود که به صورت یک اشارهگر (Pointer) در حافظه پیادهسازی میگردد.

انتساب شی دیگری (که میتواند از هر نوع دیگری باشد) به یک متغیر موجود؛ موجب حذف ارجاع قبلی آن و ارجاع به شی جدید میشود. دستور a = 2
موجب ایجاد شی 2
، حذف ارجاع متغیر a
به شی 1
و ایجاد ارجاعی جدید از متغیر a
به شی 2
میشود. هر متغیر نامی است برای اشاره به یک شی؛ دستور b = a
نیز میگوید: یک متغیر جدید با نام b
ایجاد گردد و به همان شیای ارجاع داشته باشد که متغیر a
ارجاع دارد.
ولی اکنون که ارجاعی به شی 1
وجود ندارد، با آن چه میشود؟
هر شی شامل یک «شمارنده ارجاع» (Reference Counter) نیز هست؛ به این صورت که در هر لحظه تعداد ارجاعها به آن شی را نشان میدهد و با هر ارجاع جدید به شی، یک واحد به آن اضافه میشود و با حذف هر ارجاع نیز یک واحد کاهش مییابد. چنانچه مقدار آن به صفر برسد، شی آن توسط تکنیک ”Garbage Collection“ پاک میگردد و مقدار حافظهای که توسط شی مصرف شده بود آزاد میگردد. برای مشاهده تعداد ارجاعها به یک شی میتوان از تابع
()getrefcount
درون ماژولsys
استفاده کرد [اسناد پایتون].البته مفسر پایتون اعداد صحیح و رشتههای کوچک را پس از اینکه مقدار شمارنده ارجاع آنها به صفر برسد از حافظه پاک نمیکند. هدف از این کار صرفه جویی در هزینه ایجاد این اشیا برای استفاده در آینده است. بنابراین در پاسخ به سوال بالا باید گفت که: شی
1
در حافظه باقی میماند.>>> import sys >>> a = 1 >>> sys.getrefcount(1) 760 >>> a = 2 >>> sys.getrefcount(1) 759 >>> sys.getrefcount(2) 96 >>> b = a >>> sys.getrefcount(2) 97در نمونه کد بالا همانطور که مشاهده مینمایید تعداد ارجاعها به شی
1
و2
خارج از حد انتظار است که نشان میدهد در پشت صحنه اجرای مفسر پایتون نیز ارجاعهای دیگری به این اشیا وجود دارد.
انتساب چندگانه¶
امکان ایجاد همزمان چند متغیر یا انتسابهای چندگانه در پایتون وجود دارد - میتوان چند متغیر که همگی به یک شی ارجاع دارند را ایجاد کرد:
>>> a = b = c = "python"
>>> a
'python'
>>> b
'python'
>>> c
'python'
برای انتساب اشیا متفاوت میبایست از ویرگول (Comma) و تنها یک عملگر انتساب (=
) استفاده نماییم - توجه داشته باشید که تعداد عناصر دو طرف عملگر انتساب میبایست برابر باشد:
>>> a, b, c, d = 1, 4.5, "python", 2
>>> a
1
>>> b
4.5
>>> c
'python'
>>> d
2
یکی از کاربردهای انتساب چندگانه این است که میتوان اشیا دو متغیر را به سادگی و تنها با یک سطر دستور با یکدیگر عوض کرد:
>>> a = 1
>>> b = 2
>>> a, b = b, a
>>> a
2
>>> b
1
ثابت¶
«ثابت» (Constant) به متغیری گفته میشود که مقدار آن همواره ثابت بوده و پس از تعریف دیگر امکان تغییر مقدار آن وجود ندارد. برای نمونه یک ثابت در زبان Java به شکل پایین تعریف میگردد - پس از دستور پایین هر گونه تلاش برای تغییر مقدار ثابت HOURS
با خطا روبرو میگردد:
1 | final int HOURS = 24;
|
در پایتون امکانی برای تعریف ثابت پیشبینی نشده است!.
علاوه بر امکان ایجاد ثابتها برخی موارد دیگر هم هست که در پایتون نادیده گرفته شده است. در واقع فرض پایتون بر این است که کاربران او افراد باهوشی هستند که از پس مشکلات برمیآیند؛ در نتیجه میگوید: من به کاربرانم اعتماد دارم پس نیازی نیست که تمام کارها را من برای آنها انجام دهم، یک برنامهنویس باید بتواند ریسک کند!.
ولی برای ایجاد ثابت میتوانید متغیرهای مورد نظر خود را در ماژولی جدا تعریف نمایید و در هر جایی که لازم بود با import
آن به متغیرهای مورد نظر خود دسترسی یابید:
1 2 3 4 | # File: constant.py
# Path: /home/saeid/Documents/MyModule
HOURS = 24
|
>>> import sys
>>> sys.path.append('/home/saeid/Documents/MyModule')
>>> import constant
>>> constant.HOURS
24
البته اگر تغییرناپذیر بودن متغیرها برایتان اهمیت ویژه دارد میتوانید ماژولی حاوی کد پایین ایجاد نمایید [منبع]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # File: constant.py
# Path: /home/saeid/Documents/MyModule
class _const:
class ConstError(TypeError): pass
def __setattr__(self, name, value):
if name in self.__dict__:
raise self.ConstError("Can't rebind const(%s)" % name)
self.__dict__[name] = value
import sys
sys.modules[__name__] = _const()
|
>>> import sys
>>> sys.path.append('/home/saeid/Documents/MyModule')
>>> import constant
>>> constant.HOURS = 24
>>> constant.HOURS
24
>>> constant.HOURS = 23
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/saeid/Documents/MyModule/constant.py", line 10, in __setattr__
raise self.ConstError("Can't rebind const(%s)" % name)
constant.ConstError: Can't rebind const(HOURS)
>>> constant.HOURS
24
درک کد کلاس const_
نیاز مطالعه درسهای کلاس و استثناها (Exceptions) دارد. ولی برای توضیحی کوتاه در این درس باید گفت که:
مفسر پایتون برای اینکه بداند کدام نام به کدام مقدار یا شی ارجاع دارد از ساختاری مشابه
{... ,name : value}
که به نوع دیکشنری (Dictionary) معروف است استفاده میکند؛ صفتهای هر شی و مقدار آنها نیز توسط چنین ساختاری نگهداری میشود که برای مشاهده این دیکشنری که در واقع همان فهرستی از صفتهای هر شی به همراه مقدار آنهاست میتوانید از صفت ویژه__dict__
استفاده نمایید. متد__setattr__
[اسناد پایتون] از متدهای ویژه است - این متدها امکانی هستند تا بتوانیم برای مواقعی خاص، رفتارهای مشخصی را تعریف نماییم -__setattr__
هر زمان که به یکی از صفتهای شیای از کلاس مقداری نسبت داده شود به صورت خودکار فراخوانی میگردد و وظیفه آن ذخیره صفتها و مقدار آنها در این دیکشنری است.در اینجا رفتار متد
__setattr__
کمی تغییر داده شده است به این صورت که بررسی میشود (سطر 9) چنانچه پیش از این صفت مورد نظر وجود نداشته باشد (یعنی: پیش از این هیچ مقداری به آن انتساب داده نشده است که بخواهد در فهرست باشد؛ تعریف متغیر را به یاد بیاورید) همراه با مقدار به فهرست صفتهای شی افزوده خواهد شد (سطر 11)؛ در غیر این صورت یک خطا گزارش میگردد که موجب توقف اجرای متد شده و در نتیجه از درج جدید در فهرست که موجب تغییر مقدار صفت مورد نظر میگردد جلوگیری خواهد شد (سطر 10).با ماژولها هم در پایتون به صورت شی برخورد میشود، پس مفسر پایتون باید بداند کدام نام ماژول به کدام شی مربوط است؛
sys.modules
یک دیکشنری حاوی تمام ماژولهایی است که در این لحظه از اجرای برنامه بارگذاری شدهاند.[__sys.modules[__name
به عضوی از این دیکشنری که نام آن__name__
است اشاره دارد. میدانیم که__name__
بیانگر نام ماژول جاری است؛ بنابراین عبارت[__sys.modules[__name
معرف نامی است که به شی ماژول constant.py ارجاع دارد. دستور سطر 14 موجب میشود تا ارجاع این نام به ماژول حذف شود و در عوض به شیای از کلاسconst_
نسبت داده شود که این عمل موجب حذف شی ماژول از حافظه میگردد (چون که دیگر ارجاعی به آن وجود ندارد). از طرفی میدانیم که باimport
هر ماژول، تمام محتویان آن اجرا می گردد؛ باimport
ماژول constant.py و پس از اجرای کدهای آن به ويژه سطر 14 همانطور که گفته شده ماژول مربوطه حذف میشود ولی کدهای آن هنوز در بایتکد باقی است. بنابراین پس ازimport
میتوان به آسانی از نام ماژول که اکنون ارجاع به شیای از کلاسconst_
دارد برای ایجاد صفتها که حکم ثابتهای ما را دارند استفاده کرد. [تمام این مفاهیم در آینده به صورت کامل بررسی خواهد شد]
نکته
[PEP 8]: برای نامگذاری ثابتها (Constants) تنها از حروف بزرگ و برای جداسازی کلمهها نیز از ـ
استفاده شود. مانند: MAX_OVERFLOW ،TOTAL و...
عملگرها¶
«عملگر» (Operator) به نمادی گفته میشود که عمل مشخصی را بر روی اشیا به انجام میرساند؛ به مانند عملگر انتساب =
که پیش از این بررسی شد. همچنین به اشیایی که عملگر بر روی آنها عملی را به انجام میرساند «عملوند» (Operand) گفته میشود. عملگرها دارای انواع مختلفی هستند که در ادامه بررسی خواهیم کرد.
۱. عملگرهای حسابی (Arithmetic Operators): +
-
*
**
/
//
%
+
جمع (Addition): مقدار عملوندهای دو طرف خود را با یکدیگر جمع میکند.2 + 1
حاصل: 3-
تفریق (Subtraction): مقدار عملوند سمت راست را از مقدار عملوند سمت چپ خود منها میکند:4 - 7
حاصل: 3*
ضرب (Multiplication): مقدار عملوندهای دو طرف خود را در یکدیگر ضرب میکند:2 * 5
حاصل: 10**
توان (Exponent): مقدار عملوند سمت چپ را به توان مقدار عملوند سمت راست خود میرساند.3 ** 2
حاصل: 8/
تقسیم (Division): مقدار عملوند سمت چپ را بر مقدار عملوند سمت راست خود تقسیم میکند و خارج قسمت را برمیگرداند:>>> # Python 3.x >>> # Python 2.x >>> 7 / 3 >>> 7 / 3 2.3333333333333335 2 >>> 12 / 3 >>> 12 / 3 4.0 4 >>> 6.0 / 2 >>> 6.0 / 2 3.0 3.0
همانطور که در نمونه کد بالا مشاهده میشود؛ در نسخههای 3x حاصل هر تقسیمی همواره به صورت عدد اعشاری محاسبه میگردد ولی در نسخههای 2x حاصل تقسیم دو عدد صحیح به همان صورت عدد صحیح محاسبه و از مقدار اعشار (در صورت وجود) صرف نظر میگردد. میتوان به صورت پایین این ویژگی را به نسخههای 2x اضافه کرد [اسناد پایتون]:
>>> # Python 2.x >>> from __future__ import division >>> 7 / 3 2.3333333333333335 >>> 12 / 3 4.0
//
تقسیم گردشده پایین (Floor Division): مقدار عملوند سمت چپ را بر مقدار عملوند سمت راست خود تقسیم میکند و خارج قسمت را با حذف مقدار اعشاری (در صورت وجود) برمیگرداند. حاصل این عملگر برای اعداد صحیح به صورت یک عدد صحیح محاسبه میگردد، به نتایج نمونه کد پایین توجه نمایید:>>> # Python 3.x >>> # Python 2.x >>> 7 // 3 >>> 7 // 3 2 2 >>> 12 // 3 >>> 12 // 3 4 4 >>> 6.0 // 2 >>> 6.0 // 2 3.0 3.0 >>> 7.0 // 3 >>> 7.0 // 3 2.0 2.0
%
باقی مانده (Modulus): مقدار عملوند سمت چپ را بر مقدار عملوند سمت راست خود تقسیم میکند و باقی مانده را برمیگرداند.3 % 7
حاصل: 1
۲. عملگرهای مقایسه (Comparison Operators): ==
=!
<>
<
>
=<
=>
==
برابر (Equal): چنانچه مقدار عملوندهای دو طرف برابر باشند،True
را برمیگرداند.1 == 3
: False=!
نابرابر (Not Equal): چنانچه مقدار عملوندهای دو طرف برابر نباشند،True
را برمیگرداند.1 =! 3
: True<>
نابرابر (Not Equal): عملکرد آن همانند=!
است ولی فقط در نسخه 2x پایتون قابل استفاده است.1 <> 3
: True<
بزرگتر از (Greater Than): چنانچه مقدار عملوند سمت چپ بزرگتر از مقدار عملوند سمت راست آن باشد،True
را برمیگرداند.5 < 3
: False>
کوچکتر از (Less Than): چنانچه مقدار عملوند سمت چپ کوچکتر از مقدار عملوند سمت راست آن باشد،True
را برمیگرداند.5 > 3
: True=<
برابر یا بزرگتر از (Greater Than or Equal): چنانچه مقدار عملوند سمت چپ برابر یا بزرگتر از مقدار عملوند سمت راست آن باشد،True
را برمیگرداند.5 =< 7
: True=>
برابر یا کوچکتر از (Less Than or Equal): چنانچه مقدار عملوند سمت چپ برابر یا کوچکتر از مقدار عملوند سمت راست آن باشد،True
را برمیگرداند.5 => 7
: False
۳. عملگرهای انتساب (Assignment Operators):
=
عملوند سمت راست را به عملوند سمت چپ خود نسبت میدهد. چنانچه یک عبارت محاسباتی در سمت راست باشد، حاصل آن را به عملوند سمت چپ نسبت میدهد:>>> a = 3 >>> b = 2 >>> c = a + b >>> c 5
=+
=-
=*
=**
=/
=//
=%
عملگرهای ترکیبی (انتساب حسابی): این عملگرها ابتدا عمل مربوط به عملگر حسابی را بر روی مقدار عملوندهای دو طرف خود به انجام میرسانند و سپس حاصل را به عملوند سمت چپ نسبت میدهند:>>> a += b >>> a 5
>>> a -= b >>> a 1
>>> a *= b >>> a 6
>>> a **= b >>> a 9
>>> # Python 3.x >>> # Python 2.x >>> a /= b >>> a /= b >>> a >>> a 1.5 1
۴. عملگرهای بیتی (Bitwise Operators): &
|
^
~
>>
<<
این دسته از عملگرها، عمل مشخصی را بر روی تک تک بیتهای عملوند(ها) انجام میدهند. در پایتون برای استفاده از این عملگرها لزومی به تبدیل اعداد به پایه دو (دودویی یا باینری Binary) وجود ندارد ولی در اینجا برای مشاهده بهتر عملکرد آنها از اعداد دودویی استفاده کردهایم. در زبان پایتون اعداد پایه دو همواره میبایست با یک 0b
شروع شوند:
>>> a = 0b0011
>>> a
3
>>> b = 0b0010
>>> b
2
از تابع ()bin
میتوان برای به دست آوردن مقدار دودویی یک عدد دهدهی استفاده کرد؛ البته توجه داشته باشید که این تابع مقدار دودویی را در قالب متنی (نوع String) بر میگرداند - نکته دیگر اینکه در حالت تعاملی پایتون با وارد کردن اعداد دودویی، خروجی دهدهی به دست میآید:
>>> bin(10)
'0b1010'
>>> type(bin(10))
<class 'str'>
>>> 0b1010
10
&
: معادل AND بیتی است - تنها بیتهایی از خروجی آن1
خواهند بود که هر دو بیت متناظر از عملوندهای آن1
باشند:>>> a & b 0000 0011 2 0000 0010 (AND) ----------- 0000 0010 = 2
|
: معادل OR بیتی است - تنها بیتهایی از خروجی آن0
خواهند بود که هر دو بیت متناظر از عملوندهای آن0
باشند:>>> a | b 0000 0011 3 0000 0010 (OR) ----------- 0000 0011 = 3
^
: معادل XOR بیتی است - تنها بیتهایی از خروجی آن1
خواهند بود که هر دو بیت متناظر از عملوندهای آن مخالف یکدیگر باشند:>>> a ^ b 0000 0011 1 0000 0010 (XOR) ----------- 0000 0001 = 1
~
معادل NOT بیتی و تک عملوندی است - هر یک از بیتهای عملوند خود را از0
به1
و برعکس تبدیل میکند:>>> ~ a 0000 0011 -4 (NOT) ----------- 1111 1100
یک شیوه برای نمایش اعداد علامت دار دودویی، همین عمل یعنی برعکس کردن بیتها (0 به 1 و 1 به 0) است که به آن نمایش «مکمل یک» (One's Complement) اعداد دودویی گفته میشود. ولی مفسر پایتون به صورت پیشفرض اعداد علامت دار را به شیوه رایجتر دیگری ارزیابی میکند که به نام نمایش «مکمل دو» (Two's Complement) شناخته میشود؛ در این روش پس از برعکس شدن بیتها، حاصل با عدد
1
جمع میشود. بنابراین در نمونه کد بالا حاصلNOT
عددa
برابر11111100
میشود که نمایش عدد4 -
در پایه دو به شیوه مکمل دو است:n = 3 0000 0011 3 = 0000 0011 (NOT) ----------- 1111 1100 = -3 (in One’s Complement) = -4 (in Two’s Complement)
Two’s Complement n = 4 0000 0100 4 = 0000 0100 (NOT) ----------- 1111 1011 1 ( + ) ----------- 1111 1100 = -4
میتوان مقدار
a ~
را برابر حاصل عبارت1 - a-
در نظر گرفت. بنابراین:>>> ~ a + 1 -3
>>
شیفت چپ (Left Shift): بیتهای عملوند سمت چپ را به مقدار عملوند سمت راست خود به سمت چپ جابهجا میکند - مکانهای رد شده با صفر مقداردهی میشوند:>>> a << 3 0000 0011 24 (LSH) ----------- 0001 1000 = 24
<<
شیفت راست (Right Shift): بیتهای عملوند سمت چپ را به مقدار عملوند سمت راست خود به سمت راست جابهجا میکند - مکانهای رد شده با صفر مقداردهی میشوند:>>> a = 88 0101 1000 >>> a >> 3 (RSH) ----------- 11 0000 1011 = 11
=&
=|
=^
=>>
=<<
عملگرهای ترکیبی (انتساب بیتی): این عملگرها ابتدا عمل مربوط به عملگر بیتی را بر روی عملوندهای دو طرف خود به انجام میرسانند و سپس حاصل را به عملوند سمت چپ نسبت میدهند.
۵. عملگرهای منطقی (Logical Operators):
این عملگرها عبارتند از not
or
and
که در دستورات شرطی کاربرد دارند. عملگرهای منطقی عملوندهای خود را بر اساس ارزشهای True
(درست) و False
(نادرست) مورد ارزیابی قرار میدهند و نتایج خود را بر اساس جدول پایین برمیگردانند. عملگر not
تک عملوندی است.
a b a and b a or b not a not b True False False True False True False True False True True False False False False False True True True True True True False False
عملگر and
تنها زمانی که هر دو عملوند آن ارزش True داشته باشند، True
را بر میگرداند. عملگر or
تنها زمانی که هر دو عملوند آن ارزش False داشته باشند، False
را برمیگرداند. عملگر not
نیز ارزش عملود خود را برعکس میکند (True به False و False به True).
۶. عملگرهای عضویت (Membership Operators):
شامل دو عملگر in
و not in
میباشد که از آنها برای بررسی وجود یک مقدار در میان اعضای یک دنباله (sequence مانند: رشته، لیست و...) استفاده میشود.
in
: اگر مقدار عملوند سمت چپ خود را در عملوند سمت راست بیابد،True
و در غیر این صورتFalse
را بر میگرداند.not in
: اگر مقدار عملوند سمت چپ خود را در عملوند سمت راست نیابد،True
و در غیر این صورتFalse
را بر میگرداند.
>>> "py" in "python"
True
>>> 1 in [1, 2, 3]
True
>>> "s" not in "python"
True
>>> 3 not in [1, 2, 3]
False
۷. عملگرهای هویت (Identity Operators):
شامل دو عملگر is
و is not
است که از آنها برای بررسی یکی بودن دو شی استفاده میشود.
is
: اگر هر دو عملوند به یک شی ارجاع داشته باشند،True
و در غیر این صورتFalse
را بر میگرداند.is not
: اگر هر دو عملوند به یک شی ارجاع نداشته باشند،True
و در غیر این صورتFalse
را بر میگرداند.
>>> a = 3
>>> a is 3
True
اولویتها
چنانچه عبارتی شامل چندین عملگر باشد؛ اینکه ابتدا عمل کدامیک بررسی شود، در حاصل نتیجه تاثیرگذار خواهد بود. هر کدام از عملگرها اولویتی دارند که میبایست بر اساس آن به ترتیب بررسی شوند. در بخش پایین به بررسی اولویت عملگرها خواهیم پرداخت - اولویت از بالا به پایین کاهش میابد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ( ) # پرانتز
**
~
- + # منفی و مثبت
* / // %
- + # بعلاوه و منها
<< >>
& ^ |
== != <>
< <= > >=
= **= /= //= %= *= -= +=
is is not
in in not
not and or
|
پرانتز بالاترین اولویت را دارد به این معنی که هر عبارتی داخل آن قرار بگیرد اولویت بیشتری برای بررسی خواهد داشت؛ در پرانتزهای تودرتو نیز اولویت داخلیترین پرانتز از همه بیشتر است. چنانچه عبارتی شامل چند عملگر هم سطح باشد؛ اولویت عملگر سمت چپتر بیشتر است. به عبارتهای پایین و حاصل آنها توجه نمایید:
>>> # Python 3.x
>>> 4 + 2 - 3 + 2 * 5
13
>>> 4 + ((2 - 3) + 2) * 5
9
>>> 9 / 3 * 2
6.0
>>> 3 * 2 / 9
0.6666666666666666
>>> (5 - 3) ** (7 - 3)
16
>>> 4 + 3 ** 2 - 9 / 3 * 3
4.0
>>> 4 * 2 == 5 + 3
True
عملگر شیرماهی (Walrus Operator)¶
از نسخه 3.8 پایتون یک عملگر جدید به نام Assignment Expressions یا Walrus Operator به سینتکس پایتون اضافه شده است. [PEP 572]. نمایش این عملگر به شکل =:
(شبیه دو چشم و عاج یک شیرماهی! [تصویر]) میباشد و به ما این امکان را میدهد که عملیات انتساب و بازگرداندن مقدار را به صورت همزمان به انجام برسانیم. به نمونه کد زیر توجه نمایید:
>>> walrus = False
>>> print(walrus)
False
>>> print(walrus := True) # Python >= 3.8
True
>>> walrus
True
دو نمونه کد بالا عملکرد یکسانی دارند با این تفاوت که در نمونه دوم تنها با استفاده یک سطر کد، متغیر walrus
با انتساب مقدار True
ایجاد و سپس به تابع print
ارسال میگردد.
😊 امیدوارم مفید بوده باشه
لطفا دیدگاه و سوالهای مرتبط با این درس خود را در کدرز مطرح نمایید.