لیست مطالب

عملگرها در پایتون | سیر تا پیاز

عملگرها در پایتون

operators یا عملگرها در پایتون، نشانه‌هایی هستند که به کامپیوتر می‌گویند چه کاری انجام دهد. مثلاً با + می‌توان دو عدد را با هم جمع کرد، یا با > بررسی کرد که آیا یک عدد از عدد دیگر بزرگ‌تر است یا نه. عملگرهایی هم برای مقایسه، شرط‌ها و حتی ترکیب متن وجود دارد. مثلاً با == می‌پرسیم آیا دو چیز برابرند، و با and می‌گوییم هر دو شرط باید درست باشند. استفاده از عملگرها پایه‌ی بسیاری از کارهای برنامه‌نویسی است و کمک می‌کند تا برنامه‌ها تصمیم بگیرند یا محاسبات انجام دهند.

عملگر انتساب (=)  در پایتون

اولین و ساده‌ترین عملی که تقریباً در هر زبان برنامه‌نویسی با آن روبه‌رو می‌شویم، انتساب(assign) کردن مقدار به یک متغیر است. در پایتون از علامت مساوی (=) به عنوان عملگر انتساب(assign) استفاده می‌کنیم. شکل کلی یک دستور انتساب به‌صورت زیر است:

variable = expression

 

در اینجا variable نام متغیری است که می‌خواهیم یک مقدار به آن بدهیم، و expression هر مقدار یا عبارتی است که ارزیابی شده و نتیجه‌ای تولید می‌کند (مثل یک عدد ثابت، یک عبارت حسابی، نتیجه‌ی یک تابع و …). هنگام اجرا، پایتون ابتدا عبارت سمت راست = را ارزیابی می‌کند تا یک شیء (مقدار) به‌دست آید. سپس آن شیء را در متغیر سمت چپ ذخیره می‌کند (در واقع متغیر را به آن شیء اشاره می‌دهد). به عنوان مثال:

x = 5       # متغیر x به عدد ۵ اشاره می‌کند

				
					
x = 5       # متغیر x به عدد ۵ اشاره می‌کند
day = "Friday"   # متغیر day به رشته‌ی "Friday" اشاره می‌کند
numbers = [1, 2, 3]  # متغیر numbers به لیست [1, 2, 3] اشاره می‌کند

				
			
  1. متغیر x به عدد ۵ اشاره می‌کند
  2.  متغیر day به رشته‌ی “Friday” اشاره می‌کند
  3. متغیر numbers به لیست [1, 2, 3] اشاره می‌کند

در هر یک از دستورات بالا، یک متغیر جدید ساخته شده و به شیء مشخصی در حافظه اشاره داده می‌شود.

انتساب یک مقدار به چند متغیر (انتساب چندگانه)

پایتون اجازه می‌دهد چند متغیر را در یک خط مقداردهی کنید. برای مثال:

				
					a = b = c = 0
				
			

در این خط، هر سه متغیر a، b و c به مقدار 0 انتساب داده می‌شوند.

انتساب همزمان چند مقدار (unpacking)

قابلیت جالب دیگر، انتساب چند مقدار به چند متغیر به صورت همزمان است. برای مثال:

				
					x, y, z = 1, 2, 3
				
			

بعد از اجرای این خط، x برابر ۱، y برابر ۲ و z برابر ۳ خواهد بود. این روش که گاهی “unpacking” نامیده می‌شود، کد را خلاصه‌تر و خواناتر می‌کند. برای درک عمیق تر این بخش تاپل ها در پایتون را مطالعه کنید.

می‌توانید از آن برای تعویض مقادیر دو متغیر نیز بهره ببرید:

				
					a, b = b, a   # جابجا میشوند a , b مقادیر
				
			

پس از این خط، مقدار قبلی a به b و مقدار قبلی b به a انتساب می‌یابد. بدون این قابلیت، مجبور بودیم از یک متغیر موقت برای سواپ کردن استفاده کنیم.

توجه: نمی‌توانید از عبارت‌هایی مانند if (x = 5): استفاده کنید؛ زیرا x = 5 یک دستور است نه یک مقدار منطقی. (در ادامه خواهیم دید که عملگر ویژه‌ای به نام والروس معرفی شده که این محدودیت را در موارد خاص برطرف می‌کند.)

حالا که با اصول ابتدایی انتساب آشنا شدیم، به سراغ سایر عملگرهای پرکاربرد پایتون می‌رویم.

عملگرهای حسابی (Arithmetic Operators) پایتون

عملگرهای حسابی امکان انجام عملیات ریاضی را بر روی انواع داده‌ی عددی فراهم می‌کنند. در پایتون این عملگرها شامل موارد آشنایی مانند جمع (+)، تفریق (-), ضرب (*) و … هستند که احتمالاً در ریاضی مدرسه هم دیده‌اید. جدول زیر تمامی عملگرهای حسابی پایتون را نشان می‌دهد:

عملگرشرح عملیاتمثالنتیجه‌ی مثال
+علامت مثبت (تغییری نمی‌دهد)+aمانند a (بدون تغییر)
-عدد را برعکس می‌کند (مثبت را منفی و برعکس)، ولی خود متغیر تغییر نمی‌کند-aاگر a برابر ۵ باشد، نتیجه -5 است
+جمع دو مقدارa + b5 + 2 = 7
-تفریق مقدار دوم از اولa - b5 - 2 = 3
*ضرب دو مقدارa * b5 * 2 = 10
/تقسیم (نتیجه اعشاری)a / b5 / 2 = 2.5
//تقسیم صحیح (بدون اعشار)a // b5 // 2 = 2
%باقی‌مانده تقسیمa % b5 % 2 = 1
**توان (a به توان b)a ** b5 ** 2 = 25

تقسیم اعشاری (/) در پایتون

در پایتون، عملگر تقسیم / همیشه نتیجه‌ای از نوع اعشاری (float) تولید می‌کند، حتی اگر حاصل تقسیم بدون باقی‌مانده باشد. برای مثال، عبارت 10 / 5 مقدار 2.0 را برمی‌گرداند، نه 2. این رفتار برای یکدست‌سازی نتایج تقسیم طراحی شده است. همچنین، اگر هر دو عدد اعشاری باشند، مثل 10.0 / 5، خروجی همچنان 2.0 خواهد بود.

تقسیم صحیح (//) در پایتون

عملگر // برای به‌دست آوردن بخش صحیح تقسیم استفاده می‌شود و نتیجه را به سمت کوچک‌ترین عدد صحیح بعدی گرد می‌کند. مثلاً در عبارت 7 // 3 مقدار 2 برگردانده می‌شود، چون عدد 2.33… به سمت پایین گرد می‌شود. همین رفتار برای اعداد منفی هم وجود دارد؛ مثلاً -7 // 3 برابر با -3 خواهد بود، چون مقدار -2.33… به سمت عدد پایین‌تر گرد می‌شود.

باقی‌مانده (%) در پایتون

عملگر % باقی‌مانده تقسیم را محاسبه می‌کند، اما با یک تفاوت مهم: علامت نتیجه همواره با مقسوم‌علیه (عدد دوم) هماهنگ است. مثلاً در 5 % 2 خروجی 1 است و در 5 % -2 نتیجه -1 خواهد بود، چون علامت 2- به باقی‌مانده منتقل می‌شود. این نکته در محاسبات الگوریتمی می‌تواند تعیین‌کننده باشد.

عملگرهای مقایسه‌ای (Comparison Operators) در پایتون

عملگرهای مقایسه‌ای امکان مقایسه‌ی دو مقدار را فراهم می‌کنند و نتیجه‌ی این مقایسه همواره یک مقدار بولی (True یا False) است. پایتون مجموعه‌ی کاملی از این عملگرها را برای مقایسه انواع مختلف داده‌ها (اعداد، رشته‌ها، لیست‌ها و …) ارائه می‌دهد. در جدول زیر همه‌ی عملگرهای مقایسه‌ای پایتون فهرست شده‌اند:

عملگرمفهوم مقایسهمثالنتیجه‌ی مثال
==مساوی (برابری)a == bاگر مقدار a و b برابر باشد، True؛ در غیر این صورت False
!=نامساوی (نابرابری)a != bاگر a و b برابر نباشند، True؛ وگرنه False
<کوچک‌ترa < bاگر a از b کوچک‌تر باشد، True
<=کوچک‌تر یا مساویa <= bاگر a از b کوچک‌تر یا مساوی باشد، True
>بزرگ‌ترa > bاگر a از b بزرگ‌تر باشد، True
>=بزرگ‌تر یا مساویa >= bاگر a از b بزرگ‌تر یا مساوی باشد، True

همه‌ی این عملگرها دودویی هستند، به این معنی که یک عملوند در چپ و یکی در راست خود می‌گیرند. نتیجه‌ی هر مقایسه یک مقدار بولی (True یا False) خواهد بود. به عنوان مثال:

				
					
x = 10
y = 7

print(x == y)   # خروجی: False (چون 10 برابر 7 نیست)
print(x != y)   # خروجی: True  (چون 10 نابرابر 7 است)
print(x > y)    # خروجی: True  
print(x < y)    # خروجی: False 
print(x >= 10)  # خروجی: True  
print(x <= 10)  # خروجی: True 
				
			

حال بیایید نگاهی بیندازیم به جزئیات رفتار این عملگرها برای انواع داده‌ی مختلف:

مقایسه اعداد (صحیح و اعشاری) در پایتون

مقایسه اعداد صحیح (int) و اعشاری (float) در پایتون سرراست و مشابه قواعد ریاضی است. شما می‌توانید هر دو نوع را بدون مشکل با هم مقایسه کنید؛ مثلاً 5 == 5.0 نتیجه‌اش True است زیرا مقدار عددی هر دو یکی است (هرچند نوعشان یکی نیست). در واقع اعداد صحیح و اعشاری در مقایسه با هم سازگارند و پایتون در صورت نیاز عدد صحیح را به اعشاری تبدیل می‌کند تا مقایسه انجام شود.

اما یک نکته‌ی مهم درباره‌ی اعداد اعشاری (نوع float): به دلیل نحوه‌ی نمایش این اعداد در حافظه (مبنای دودویی)، ممکن است نتوانید همیشه انتظار داشته باشید که دو محاسبه‌ی اعشاری که از نظر ریاضی باید برابر باشند، دقیقاً مساوی (==) هم ارزیابی شوند. برای مثال:

				
					
x = 1.1 + 2.2
print(x == 3.3)    # احتمالاً False
print(x)           # احتمالاً چیزی مثل 3.3000000000000003

				
			

در حالت ایده‌آل 1.1 + 2.2 باید 3.3 شود، اما نتیجه در حافظه ممکن است 3.3000000000000003 ذخیره شود که با 3.3 دقیقاً برابر نیست و بنابراین مقایسه‌ی مساوی ممکن است False برگرداند. این یک مشکل رایج در نمایش اعداد اعشاری است. برای مقایسه‌ی اعشاری‌ها معمولاً بهتر است به جای == از روش‌هایی استفاده کنیم که اجازه‌ی خطای جزئی را می‌دهند؛ مثلاً استفاده از تابع math.isclose(x, y) در پایتون، که بررسی می‌کند آیا دو عدد با در نظر گرفتن یک تلورانس کوچک تقریباً برابرند یا نه.

برای اعداد صحیح چنین مشکلی وجود ندارد و مقایسه‌ها کاملاً دقیق هستند.

				
					
print("A" < "a")    # خروجی: True
print("A" == "a")   # خروجی: False
print("Ali" < "Reza")  # خروجی :‌True

				
			

در مثال بالا، “A” < “a” نتیجه‌اش True است چون کد یونی‌کد ‘A’ (عدد 65) از ‘a’ (عدد 97) کوچک‌تر است. بنابراین پایتون نتیجه می‌گیرد “A” از “a” “کوچک‌تر” است (می‌توان گفت در ترتیب واژگانی قبل از آن قرار می‌گیرد). همچنین “A” == “a” طبیعتاً False است چون حروف بزرگ و کوچک متفاوت محسوب می‌شوند.

برای رشته‌های طولانی‌تر، پایتون کاراکترهای متناظر را یکی یکی مقایسه می‌کند تا جایی که به دو کاراکتر متفاوت برسد یا یکی از رشته‌ها به انتها برسد. مثلاً:

				
					
print("Hello" < "HellO")       # خروجی: False
print("Hello" < "Hello, World!")  # خروجی: True

				
			

در رشته‌ی “Hello” و “HellO” فقط حرف آخرشان متفاوت است (“o” کد 111 در یونیکد و “O” کد 79). چون 111 بزرگ‌تر از 79 است، نتیجه‌ی مقایسه نشان می‌دهد کل رشته‌ی “Hello” (که آخرین حرفش کد بالاتری دارد) از “HellO” بزرگ‌تر است.

در مقایسه‌ی “Hello” با “!Hello, World”، تمام حروف “Hello” در هر دو رشته یکسان است، اما رشته‌ی دوم کاراکترهای اضافه (!World) دارد در حالی که رشته‌ی اول پایان یافته. در چنین حالتی رشته‌ی کوتاه‌تر، کوچک‌تر در نظر گرفته می‌شود. بنابراین “Hello” از “!Hello, World” کوچک‌تر است (چون به نوعی در مرتب‌سازی لغت‌نامه‌ای، رشته‌ی کوتاه‌تر جلوتر می‌آید اگر مقدمه‌ی یکسانی داشته باشند). همچنین رشته‌ی خالی “” کوچک‌ترین رشته‌ی ممکن محسوب می‌شود.

مقایسه لیست‌ها و تاپل‌ها (Lists & Tuples) در پایتون

شاید جالب باشد بدانید که حتی لیست‌ها و تاپل‌ها را نیز می‌توان با عملگرهای مقایسه‌ای سنجید، البته به شرطی که نوع دو طرف یکی باشد (مثلاً هر دو لیست یا هر دو تاپل). روش مقایسه‌ی لیست‌ها و تاپل‌ها نیز مانند رشته‌ها عضو به عضو و به صورت لغت‌نامه‌ای (lexicographical) است. یعنی ابتدا اولین عناصر دو دنباله مقایسه می‌شوند؛ اگر یکی کوچکتر بود تکلیف مشخص می‌شود. در غیر این صورت سراغ عنصر بعدی می‌رود و … تا جایی که دو مقدار متفاوت پیدا شود یا یکی از دنباله‌ها به انتها برسد.

مثال‌ها:

				
					
print([2, 3] == [2, 3])   # خروجی: True (دو لیست یکسان‌اند)
print((1, 2, 3) < (1, 2, 5))  # خروجی: True (در تاپل دوم عنصر سوم 5 است که 3 < 5)
print([1, 2] < [1, 2, 0])    # خروجی: True (لیست اول زیرلیست لیست دوم است و کوتاه‌تر است)
print([2, 3] == (2, 3))      # خروجی: False (نوعشان متفاوت است)
print([2, 3] < (2, 3))       # TypeError (نمی‌توان لیست و تاپل را با < سنجید)

				
			

به طور خلاصه:

  • دو لیست یا دو تاپل وقتی مساوی‌اند که نوع یکسان، طول یکسان و تمامی عناصر متناظرشان با هم مساوی باشند.
  • در مقایسه‌ی ترتیب‌دار (<  >)، ابتدا اولین عنصر متفاوت تعیین‌کننده است و اگر همه‌ی عناصر یکسان بودند اما یکی از دنباله‌ها کوتاه‌تر بود، دنباله‌ی کوتاه‌تر کوچک‌تر محسوب می‌شود.
  • در پایتون۳ ، مقایسه‌های ترتیبی (< , >, <= , >=) بین لیست و تاپل یا لیست و رشته پشتیبانی نمی‌شود و با TypeError مواجه می‌شوند.

عملگرهای مقایسه‌ای در تصمیم‌گیری‌ها

معمولاً از نتایج مقایسه‌ها در عبارات شرطی (ifها) یا حلقه‌ها (while) استفاده می‌شود تا بر اساس True/False بودن آن‌ها، مسیر برنامه تعیین شود. به عنوان مثال:

				
					
age = 18
if age >= 18:
    print("شما بزرگسال هستید")
else:
    print("شما هنوز به سن قانونی نرسیده‌اید")

				
			

اینجا شرط age >= 18 یک عبارت مقایسه‌ای است که نتیجه‌اش True یا False خواهد بود و بر اساس آن یکی از دو شاخه‌ی if یا else اجرا می‌شود.

در ادامه با عملگرهای منطقی آشنا می‌شویم که می‌توانند نتایج چند مقایسه را با هم ترکیب کنند.

عملگرهای منطقی (Boolean Operators) و عبارات بولی

عملگرهای منطقی مثل سه کلید ساده هستند که به‌کمکشان می‌توانیم تصمیم‌های ترکیبی بگیریم:

  • و (and): وقتی می‌خواهیم دو شرط با هم برقرار باشند، مثل «وقتی هوا آفتابی باشد و وقت آزاد داشته باشم، به پیاده‌روی می‌روم».
  • یا (or): وقتی فقط یکی از چند شرط کافی است، مثل «اگر باران ببارد یا برف بیاید، کتری را روشن می‌کنم».
  • نفی (not): وقتی می‌خواهیم بگوییم چیزی برقرار نیست، مثل «اگر ترافیک نباشد، سر ساعت به قرار می‌رسم».

     

این سه کلید کمک می‌کنند شرایط زندگی روزمره را در ذهن یا در کد خودتان به راحتی ترکیب و بررسی کنید.

				
					
is_student = True
is_registered = False

print(is_student and is_registered)  # خروجی: False
print(is_student or is_registered)   # خروجی: True  
print(not is_student)                # خروجی: False

				
			

در این مثال، فرض کردیم is_student (آیا کاربر دانشجو است) True و is_registered (آیا ثبت‌نام کرده) False است. آنگاه:

  • and هر دو شرط را نیاز دارد، پس نتیجه False شد چون is_registered False بود.
  • or کافیست یکی True باشد، پس نتیجه True شد چون is_student True بود.
  • not is_student هم عکس مقدار is_student یعنی False شده است.
برای درک عمیق تر، عبارات شرطی در پایتون را مطالعه کنید.

عملگرهای هویت (Identity Operators) در پایتون

فرض کنید یک لیوان چای داغ در اختیار دارید.

اگر با هر دو دست لبه‌های همان لیوان را بگیرید، هر دو دست دقیقاً به یک شیء واحد اشاره می‌کنند؛ این وضعیت معادل is است، یعنی لیوان در دست راست همان لیوان در دست چپ است.

اما اگر دو لیوان یکسان اما جداگانه برداشته و هر یک را با یکی از دست‌ها بگیرید، هر دو لیوان محتوا و ظاهر یکسانی دارند ولی دو شیء مستقل هستند؛ این وضعیت معادل == است، یعنی محتوا یکی است اما هویت متفاوت.

 

  • عبارت x is y زمانی مقدار True می‌گیرد که x و y دقیقاً به یک آدرس حافظه اشاره کنند.
  • عبارت x is not y زمانی True است که هر یک به شیء جداگانه‌ای اشاره نمایند.

     

توجه داشته باشید که «برابری» (==) با «هویت» (is) متفاوت است: برابری به شباهت محتوا می‌پردازد، اما هویت تأیید می‌کند که دقیقاً یک شیء واحد در حافظه استفاده شده است.

برای روشن شدن موضوع، به مثال زیر دقت کنید:

				
					
a = 1001
b = 1001
print(a == b)   # خروجی: True
print(a is b)   # خروجی: False

				
			

در اینجا متغیرهای a و b هر دو مقدار 1001 را دارند، پس بدیهی است که a == b نتیجه True می‌دهد (محتوای آن‌ها برابر است). اما آیا هر دوی این‌ها به یک مکان در حافظه اشاره می‌کنند؟ پایتون برای بهینه‌سازی، اعداد کوچک را ممکن است کَش (cache) کند اما برای اعداد بزرگ‌تر هر بار شیء جداگانه می‌سازد. در این مثال احتمالاً دو شیء مجزا با مقدار 1001 ساخته شده و به a و b اختصاص یافته است. لذا a is b نتیجه False می‌دهد. می‌توان این را با تابع داخلی ()id هم بررسی کرد که شناسه‌ی یکتای هر شیء (آدرس در حافظه) را برمی‌گرداند:

				
					
print(id(a))
print(id(b))
# این دو عدد چاپ‌شده احتمالاً متفاوت خواهند بود
   # خروجی: False

				
			

برای اطمینان از فهم موضوع، مثال دیگری:

				
					
x = [1, 2, 3]
y = x
print(x == y)  # خروجی: True
print(x is y)  # خروجی: True

				
			

اینجا ما ابتدا لیستی [1,2,3] در x داشتیم. سپس نوشتیم y = x. برخلاف مثال قبل که دو شیء مجزا ساخته بودیم، اینجا y را به همان شیء x اشاره دادیم (یعنی هر دو نام به یک لیست مشترک در حافظه اشاره دارند). بنابراین بدیهی است که هم == True است (محتوا یکسان چون در حقیقت یک لیست است) و هم True ,is است (چون دقیقاً یک شیء هستند).

نکته: برای چک کردن اینکه یک متغیر None هست یا نه از is None یا is not None استفاده کنید (به جای ==). زیرا None شیء یکتایی است و قرار نیست کپی داشته باشد. نوشتن if my_var is None: هم خواناتر است و هم دقیقاً همان چیزی را می‌رساند که می‌خواهید.

عملگرهای عضویت (Membership Operators) در پایتون

دو عملگر in و not in برای آزمون عضویت به کار می‌روند. به زبان ساده، می‌خواهند بپرسند «آیا یک مقدار در یک مجموعه (دنباله) وجود دارد یا خیر؟». این مجموعه می‌تواند یک لیست، تاپل، مجموعه (set)، دیکشنری یا حتی یک رشته باشد.

تعاریف:

  • True <- value in collection است اگر عضوی مساوی با value در مجموعه‌ی collection وجود داشته باشد.
  • False <- value not in collection است اگر چنین عضوی در مجموعه نباشد (در واقع نتیجه‌ی نقیض حالت بالا).
برای مثال:
				
					
nums = [2, 3, 5, 7]
print(5 in nums)     # خروجی: True (پنج یکی از اعضای لیست است)
print(8 in nums)     # خروجی: False (هشت در لیست وجود ندارد)
print(8 not in nums) # خروجی: True (هشت عضو لیست نیست)

				
			

برخی کاربردهای دیگر:

  • برای رشته‌ها، عملگر in بررسی می‌کند که آیا یک رشته‌ی فرعی (substring) در رشته‌ی بزرگ‌تر وجود دارد یا نه. مثلاً “ali” in “salam ali” نتیجه True دارد چون رشته “ali” به عنوان زیررشته وجود دارد.
  • برای دیکشنری‌ها، in روی کلیدها عمل می‌کند. یعنی key in my_dict True است اگر کلیدی برابر با key در دیکشنری باشد.
  • برای مجموعه‌ها (set) و سایر ساختمان‌های داده نیز به طور مشابه عمل می‌کند.

این عملگرها هم در شرایط شرطی بسیار به کار می‌آیند. مثلاً:

				
					
allowed_users = ["alice", "bob", "charlie"]
user = "david"
if user not in allowed_users:
    print("دسترسی برای این کاربر مجاز نیست!")

				
			

عملگرهای اتصال و تکرار برای دنباله‌ها (Concatenation + و Repetition *)

عملگرهای + و * علاوه بر معنای حسابی برای اعداد، وقتی روی نوع دنباله (sequence type) مثل رشته، لیست یا تاپل به کار روند معنای متفاوت اما مفیدی دارند:

  • عملگر + برای دنباله‌ها معنی اتصال (Concatenation) می‌دهد: یعنی دو دنباله را پشت سر هم به هم می‌چسباند و یک دنباله‌ی جدید حاصل می‌کند. برای مثال “Hello, ” + “World” نتیجه “Hello, World” است؛ یا [3,4] + [1,2] نتیجه [1,2,3,4] خواهد بود. نوع دو طرف باید یکسان باشد (نمی‌توان مثلاً لیست و رشته را با + ترکیب کرد).
  • عملگر * برای دنباله‌ها معنی تکرار (Repetition) می‌دهد: یعنی دنباله‌ی سمت چپ را به تعداد مشخص‌شده توسط عدد سمت راست، پشت سر هم تکرار می‌کند و دنباله‌ی جدیدی می‌سازد. مثلا Ha” * 3 نتیجه “HaHaHa” می‌دهد؛ یا [0] * 5 نتیجه [0, 0, 0, 0, 0] خواهد بود. (عدد *همچنین می‌تواند سمت راست باشد و دنباله سمت چپ؛ ترتیب فرقی ندارد.)

     

بنابراین اگر seq1 و seq2 هر دو رشته یا هر دو لیست باشند، seq1 + seq2 دنباله‌ای است که ابتدا عناصر seq1 و سپس عناصر seq2 را شامل می‌شود. مثال:

				
					
print("Py" + "thon")        # خروجی: "Python"
print([1,2] + [3,4,5])      #  [1, 2, 3, 4, 5]
print((10, 20) + (30,))     #  (10, 20, 30)

				
			

در اتصال تاپل‌ها در پایتون توجه کنید که برای ایجاد تاپل تک‌عضوی نیاز به کاما داریم مانند (,30). در مثال بالا (10,20) + (,30) یک تاپل (10,20,30) ساخت.

برای عملگر تکرار *:

				
					
print("Ha" * 3)    # خروجی: "HaHaHa"
print(3 * "OK")    # خروجی: "OKOKOK"  (ترتیب ضرب فرقی ندارد)
print([1, 2] * 2)  # [1, 2, 1, 2]
print((5,) * 4)    # (5, 5, 5, 5)

				
			

عملگر والروس =: (Assignment Expressions) در پایتون

عملگر = که پیش‌تر دیدیم، یک دستور است و خروجی‌ای ندارد. اما در نسخه 3.8 پایتون یک عملگر جدید معرفی شد: عملگر والروس که با نماد=: نشان داده می‌شود. نام “والروس” (فُک دریایی) از شباهت شکل =: به چشم و عاج این حیوان گرفته شده است! این عملگر به ما امکان می‌دهد داخل یک عبارت، یک انتساب انجام دهیم و همزمان از مقدار آن بهره ببریم. به همین دلیل به آن assignment expression (عبارت انتساب) هم گفته می‌شود.

برای درک بهتر، یک نمونه را در نظر بگیریم. فرض کنید می‌خواهیم طول یک لیست را بدست آوریم و اگر بیش از ۱۰ بود پیغامی چاپ کنیم:

				
					
values = [3, 7, 1, 4, 9, 0, 6, 5, 8, 2, 11]
n = len(values)
if n > 10:
    print(f"لیست خیلی بلند است ({n} عنصر دارد، حداکثر 10 مجاز است)")

				
			

در این کد، دوبار تابع len(values) را صدا زدیم: یک‌بار نتیجه را در n ریختیم، بار دوم برای استفاده در شرط if. با عملگر والروس، می‌توانیم این را در یک مرحله انجام دهیم:

				
					
if (n := len(values)) > 10:
    print(f"لیست خیلی بلند است ({n} عنصر دارد، حداکثر 10 مجاز است)")

				
			

به داخل پرانتز توجه کنید: عبارت (n := len(values)) ابتدا طول لیست را حساب می‌کند، همان را در متغیر n ذخیره می‌کند و در نهایت همان مقدار را نیز برمی‌گرداند. سپس این مقدار با ۱۰ مقایسه می‌شود. به این ترتیب با یک بار محاسبه طول، هم شرط را بررسی کرده‌ایم هم مقدار طول را برای استفاده در پیام در اختیار داریم.

بنابراین عملگر =: دو کار را همزمان انجام می‌دهد:

  1. انتساب نتیجه‌ی یک عبارت به متغیری که در سمت چپ =: آمده.
  2. برگرداندن همان نتیجه برای ادامه‌ی استفاده در عبارت بزرگ‌تر.

یکی از کاربردهای رایج والروس در حلقه‌های while است؛ به‌خصوص زمانی که می‌خواهید در هر دور حلقه ورودی‌ای بگیرید یا محاسبه‌ای کنید و همان را هم برای شرط استفاده کنید. مثلا:

				
					
# خواندن خطوط غیرخالی از فایل تا وقتی که خط خالی برسد
while (chunk := file.read(100)) != "":
    process(chunk)

				
			

در این مثال، در هر دور 100 کاراکتر از فایل خوانده می‌شود و در متغیر chunk قرار می‌گیرد، سپس چک می‌شود که آیا chunk خالی (“”) نبوده است. اگر خالی نبود، پردازش انجام می‌دهد و به ابتدای while برمی‌گردد. این شیوه کد را خلاصه‌تر می‌کند (نیاز به تکرار خواندن فایل بیرون و داخل حلقه نیست).

نکته: استفاده از والروس همیشه هم توصیه نمی‌شود. باید جایی به کار رود که واقعاً کد را خواناتر و کوتاه‌تر کند. گاهی ممکن است کد با والروس پیچیده‌تر به نظر برسد. بنابراین «بااحتیاط و در موارد تمیز» از آن استفاده کنید.

در ضمن، در بیشتر موارد لازم است اطراف یک عبارت انتسابی والروس پرانتز بگذارید (مگر در ساختارهایی مثل شرط if ساده). این یک نیاز نحوی است تا با موارد دیگر تداخل نکند

عملگرهای بیت‌به‌بیت (Bitwise Operators) در پایتون

عملگرهای بیت‌به‌بیت بر روی نمایش دودویی اعداد صحیح عمل می‌کنند و عملیات سطح بیت‌ها (0 و 1) را انجام می‌دهند. این عملگرها برای برنامه‌نویسی‌های سطح پایین‌تر، کار با بیت‌ها، پرچم‌ها (flags)، رمزنگاری، فشرده‌سازی و موارد اینچنینی استفاده می‌شوند. اگرچه ممکن است در برنامه‌های ساده و روزمره کمتر مستقیماً با بیت‌ها سروکار داشته باشید، آشنایی اولیه با این عملگرها خالی از لطف نیست.

لیست عملگرهای بیت‌به‌بیت در پایتون:

عملگرنام عملیاتتوضیح مختصر عملیات بر بیت‌ها
&AND بیت‌به‌بیت (و)هر بیت ۱ می‌شود اگر هر دو بیت متناظر ۱ باشند، در غیر این صورت ۰.
|OR بیت‌به‌بیت (یا)هر بیت ۱ می‌شود اگر حداقل یکی از بیت‌های متناظر ۱ باشد، در غیر این صورت ۰.
^XOR بیت‌به‌بیتهر بیت ۱ می‌شود اگر بیت‌های متناظر متفاوت باشند (یکی ۱ و دیگری ۰)، در غیر این صورت ۰.
~NOT بیت‌به‌بیت (نقیض)تمام بیت‌های عدد را معکوس می‌کند (۰ها ⇄ ۱ها).
<<شیفت به چپبیت‌ها را به سمت چپ انتقال می‌دهد و از سمت راست صفر وارد می‌کند؛ معادل ضرب در توان‌های ۲.
>>شیفت به راستبیت‌ها را به سمت راست انتقال می‌دهد؛ برای اعداد مثبت از سمت چپ صفر وارد می‌کند، اما برای
اعداد منفی با یک (۱) پر می‌کند تا علامت حفظ شود. معادل تقسیم بر توان‌های ۲ (با گرد به پایین).

برای درک بهتر، چند مثال ساده با اعداد کوچک انجام می‌دهیم و از نمایش باینری آن‌ها کمک می‌گیریم (در پایتون برای نمایش عدد در مبنای 2 می‌توانید از تابع ()bin استفاده کنید):

				
					x = 6   # In binary: 110
y = 3   # In binary: 011

print(bin(x))       # '0b110'
print(bin(y))       # '0b11'  (equivalent to 0b011)
print(bin(x & y))   # '0b10'  -> binary 010 = 2
print(x & y)        # 2

print(bin(x | y))   # '0b111' -> binary 111 = 7
print(x | y)        # 7

print(bin(x ^ y))   # '0b101' -> binary 101 = 5
print(x ^ y)        # 5

print(bin(~x))      # result depends on bit-width used for display
print(~x)           # equals -(x + 1) = -7

				
			

در این مثال:

x = 6 (110 در باینری) و y = 3 (011 در باینری). 

x & y: AND بیت‌به‌بیت:

110   (6)

& 011   (3)

= 010   (2)

 تنها بیت مشترک 1 در هر دو عدد، بیت دوم از راست است (۲^1 = 2). پس نتیجه 2 شد. 

 

x | y: OR بیت‌به‌بیت:

110   (6)

| 011   (3)

= 111   (7)

 هر جایی حداقل یکی 1 بود نتیجه 1 شد، که تمام سه بیت 1 شدند (7). 

 

x ^ y: XOR بیت‌به‌بیت:

110   (6)

^ 011   (3)

= 101   (5)

بیت‌های متفاوت به 1 تبدیل شدند: در اینجا بیت‌های چپ و راست متفاوت بودند (یکی‌ 1 و دیگری 0) و وسطی چون در هر دو 1 بود در نتیجه صفر شد. نتیجه 5 شد.

برای x~، باید توجه کنیم که پایتون از مدل “متمم دو” (Two’s complement) برای نمایش اعداد منفی استفاده می‌کند. در این مدل، x~ برابر است با (x+1). به همین دلیل در مثال بالا که x=6 بود، 6~ شد 7. زیرا:

6~ = (6 + 1) = 7

در عمل، x~ تمام بیت‌های x را معکوس می‌کند و نتیجه تفسیر می‌شود. جزئیات دقیق‌تر بیت‌های بینهایت علامت در اینجا خارج از بحث ماست، اما همین رابطه ساده را به خاطر بسپارید.

شیفت‌ها:

  • شیفت چپ (>>): مثلاً 2 >> 3 یعنی 3 را در مبنای 2 (که میشود 11) دو رقم به چپ ببریم: 1100 که برابر ۱۲ ده‌دهی است. عملاً شیفت چپ ۲بیتی معادل ضرب در ۴ (2^2) شد. به طور کلی x<<n ≙ x * 2^n.

     

     

شیفت راست (<<): مثلاً 1<<13 یعنی 13 (1101₂) را یک بیت به راست ببریم: 110 (که 6 است). این شبیه تقسیم بر 2 (با حذف بخش کسری) است. در کل x>>n ≙ ⌊x / 2^n⌋ برای اعداد مثبت.

تقدم عملگرها (Operator Precedence) در پایتون

حال که با انواع عملگرها آشنا شدیم، یک سوال پیش می‌آید: اگر در یک عبارت چندین عملگر مختلف وجود داشت، ترتیب اجرای آن‌ها چگونه خواهد بود؟ اینجاست که مبحث تقدم (اولویت) عملگرها مطرح می‌شود.

برای مثال به عبارت زیر نگاه کنید:

result = 20 + 4 * 10

 

آیا این محاسبه را باید این طور تفسیر کرد که اول ۲۰+۴ = ۲۴ شود و سپس ۲۴۱۰ = ۲۴۰؟ یا برعکس، اول ۴۱۰ = ۴۰ حساب شود و بعد ۲۰+۴۰ = ۶۰؟ پاسخ درست ۶۰ است. پایتون (و اکثر زبان‌ها) عمل ضرب را قبل از جمع انجام می‌دهند، مگر اینکه شما با پرانتز خلاف آن را مشخص کنید. این بر اساس تقدم پیش‌فرض عملگرها است که مشابه قواعد ریاضی معمول (اولویت ضرب و تقسیم بر جمع و تفریق) عمل می‌کند.

به طور کلی، در پایتون نیز هر عملگری یک سطح تقدم دارد. عملگرهای با اولویت بالاتر زودتر اجرا می‌شوند و عملگرهای هم‌سطح نیز از چپ به راست (برای اکثر عملگرها) یا راست به چپ (برای چند مورد خاص مثل توان) ارزیابی می‌شوند.

جدول زیر ترتیب کلی تقدم عملگرهایی که تاکنون یاد گرفتیم را از بالاترین به پایین‌ترین نشان می‌دهد (هر ردیف یک سطح است، عملگرهای یک ردیف تقدم یکسان دارند):

سطح تقدم ↓عملگرها (از راست به چپ هر ردیف برابرند)توضیح
بالاترین**توان (تنها عملگر راست‌به‌چپ)
+x, -x, ~xمثبت/منفی یگانی و NOT بیت‌به‌بیت
*, /, //, %ضرب، تقسیم، تقسیم صحیح، باقیمانده
+, -جمع و تفریق
<<, >>شیفت چپ و راست
&AND بیت‌به‌بیت
^XOR بیت‌به‌بیت
|OR بیت‌به‌بیت
==, !=, <, <=, >, >=, is, is not, in, not inمقایسه‌ها، عضویت و هویت (هم‌سطح)
notنقیض منطقی
andAND منطقی
orOR منطقی
پایین‌ترین:=والروس (انتساب در عبارت)

در جدول بالا، عملگرهایی که در یک ردیف هستند، تقدم یکسان دارند و اگر با هم در یک عبارت بیایند، ارزیابی آن‌ها از چپ به راست انجام می‌شود (به جز ** که راست‌به‌چپ بود همان‌طور که ذکر شد).

مثال: در عبارت ۲ + ۳ – ۵ که هم – و هم + در یک سطح‌اند، پایتون از چپ به راست می‌رود: اول ۲ = ۳ – ۵، بعد ۴ = ۲ + ۲.

باز هم تاکید می‌کنیم، اگر در ترکیب یک عبارت اولویت‌ها مبهم بود، یا حتی اگر واضح هم بود ولی برای خواننده ممکن است گیج‌کننده باشد، همیشه می‌توانید و توصیه می‌شود از پرانتز استفاده کنید. پرانتز‌ها تقدمی بالاتر از همه دارند و باعث می‌شوند زیرعبارات داخلشان ابتدا ارزیابی شوند. این کار نه تنها ترتیب دلخواه شما را اعمال می‌کند، بلکه برای دیگران (و خودتان در آینده) فهمیدن قصد کد را راحت‌تر می‌کند.

مثال:

				
					
result1 = 2 * 3 ** 4 * 5      # بدون پرانتز
result2 = 2 * (3 ** 4) * 5    # با پرانتز مشخص
result3 = (2 * 3) ** (4 * 5)  # پرانتزبندی کاملاً متفاوت
print(result1, result2, result3)  # 810 810 ** (the last result is a very large number: 6 to the power of 20)


				
			

عملگرهای انتساب ترکیبی (Augmented Assignment)

در پایان، به دسته‌ای از عملگرها می‌پردازیم که ترکیبی از انتساب و سایر عملگرها هستند. این‌ها شکل خلاصه‌ی مثال‌هایی مانند x = x + y یا x = x * y هستند و احتمالاً در زبان‌های دیگر هم با آن‌ها آشنا هستید:

  • += (جمع و انتساب): معادل x = x + y
  • -= (تفریق و انتساب): معادل x = x – y
  • *= (ضرب و انتساب): معادل x = x * y
  • /= (تقسیم و انتساب): معادل x = x / y
  • //= (تقسیم صحیح و انتساب): معادل x = x // y
  • %= (باقی‌مانده و انتساب): معادل x = x % y
  • **= (توان و انتساب): معادل x = x ** y

نسخه‌های ترکیبی دیگری نیز برای عملگرهای بیت‌به‌بیت تعریف شده‌اند:

  • &=, |=, ^=, <<=, >>= که به‌ترتیب معادل x = x & y و … هستند.

و حتی برای دنباله‌ها:

  • += روی رشته یا لیست به معنی الحاق و سپس انتساب است؛ در رشته نسخه‌ی جدید ساخته می‌شود و در خود رشته ذخیره می‌شود، و در لیست بسط درجا انجام می‌گیرد.
  • *= روی دنباله به معنی تکرار و انتساب است.

چند مثال برای درک بهتر:

				
					
count = 0
count += 1   # count = count + 1 -> حالا count برابر 1 است

balance = 1000
balance -= 50   # balance = 1000 - 50 = 950

s = "Hello"
s += " World"
print(s)    # خروجی: "Hello World"

numbers = [1, 2, 3]
numbers *= 2
print(numbers) #  [1, 2, 3, 1, 2, 3]

				
			

این عملگرها باعث می‌شوند کد ما کوتاه‌تر و تمیزتر شود و جلوی تکرار اضافه را بگیرند. تصور کنید اگر قرار باشد ۱۰ جای مختلف در برنامه مقدار متغیری را اضافه کنیم، به جای … + x = x می‌توانیم از =+ استفاده کنیم که هم خواناتر است (حس افزودن به را القا می‌کند) و هم احتمال خطای نوشتاری را کم می‌کند.

شما در این مقاله‌ی جامع با انواع گوناگون عملگرهای پایتون آشنا شدید: از ابتدایی‌ترین (انتساب، حسابی و مقایسه‌ای) گرفته تا منطقی، عضویت، هویت، عملگر والروس جدید، بیت‌به‌بیت و انتساب ترکیبی. همچنین یاد گرفتید که هر کدام چگونه در عبارات عمل می‌کنند و در چه مواقعی استفاده می‌شوند.

اکنون می‌توانید با دید بازتری کدهای پایتون را بخوانید و بنویسید. پیشنهاد می‌کنیم برای تسلط بیشتر، مثال‌های آورده‌شده را خودتان اجرا و شاید تغییر دهید، و تاثیر هر عملگر را به صورت عملی مشاهده کنید. تمرین و تجربه بهترین راه یادگیری و به خاطر سپردن این مفاهیم است.

 

نوشته های مرتبط

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *