Python پایتون ۳.۱۴
فصل ۵

ساختارهای داده (Data Structures)

📖 ۱۸ دقیقه 🎯 متوسط 🔄 ۲۰۲۶

این فصل برخی از مباحثی را که قبلاً یاد گرفته‌اید، با جزئیات بیشتر توضیح می‌دهد و همچنین مباحث جدیدی را اضافه می‌کند.

۵.۱. بیشتر دربارهٔ لیست‌ها (Lists)

نوع داده لیست متدهای بیشتری دارد. در اینجا تمام متدهای اشیاء لیست آورده شده است:

  • list.append(value, /) — یک آیتم به انتهای لیست اضافه می‌کند. معادل a[len(a):] = [x] است.
  • list.extend(iterable, /) — لیست را با اضافه کردن تمام آیتم‌های موجود در iterable گسترش می‌دهد. معادل a[len(a):] = iterable است.
  • list.insert(index, value, /) — یک آیتم در موقعیت مشخص‌شده درج می‌کند. اولین آرگومان، اندیس عنصری است که قبل از آن درج انجام می‌شود، بنابراین a.insert(0, x) در ابتدای لیست درج می‌کند و a.insert(len(a), x) معادل a.append(x) است.
  • list.remove(value, /) — اولین آیتم از لیست که مقدارش برابر با value است را حذف می‌کند. اگر چنین آیتمی وجود نداشته باشد، ValueError ایجاد می‌کند.
  • list.pop(index=-1, /) — آیتم موجود در موقعیت مشخص‌شده در لیست را حذف کرده و آن را برمی‌گرداند. اگر اندیس مشخص نشود، a.pop() آخرین آیتم لیست را حذف کرده و برمی‌گرداند. اگر لیست خالی باشد یا اندیس خارج از محدودهٔ لیست باشد، IndexError ایجاد می‌کند.
  • list.clear() — تمام آیتم‌ها را از لیست حذف می‌کند. معادل del a[:] است.
  • list.index(value[, start[, stop]]) — اندیس صفر-محور اولین وقوع value را در لیست برمی‌گرداند. اگر چنین آیتمی وجود نداشته باشد، ValueError ایجاد می‌کند. آرگومان‌های اختیاری start و end مانند slice notation تفسیر می‌شوند و برای محدود کردن جست‌وجو به یک زیردنبالهٔ خاص از لیست استفاده می‌شوند. اندیس برگشتی نسبت به ابتدای کل دنباله محاسبه می‌شود، نه آرگومان start.
  • list.count(value, /) — تعداد دفعاتی که value در لیست ظاهر می‌شود را برمی‌گرداند.
  • list.sort(*, key=None, reverse=False) — آیتم‌های لیست را در همان مکان (in place) مرتب می‌کند (آرگومان‌ها می‌توانند برای سفارشی‌سازی مرتب‌سازی استفاده شوند، برای توضیح آن‌ها به sorted() مراجعه کنید).
  • list.reverse() — عناصر لیست را در همان مکان (in place) معکوس می‌کند.
  • list.copy() — یک کپی سطحی (shallow copy) از لیست برمی‌گرداند. معادل a[:] است.

مثالی که بیشتر متدهای لیست را استفاده می‌کند:

>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4)  # Find next banana starting at position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'

ممکن است متوجه شده باشید که متدهایی مانند insert، remove یا sort که فقط لیست را تغییر می‌دهند، مقدار بازگشتی ندارند — آن‌ها None پیش‌فرض را برمی‌گردانند. [1] این یک اصل طراحی برای تمام ساختارهای دادهٔ قابل‌تغییر (mutable) در پایتون است.

نکتهٔ دیگری که ممکن است متوجه شوید این است که همهٔ داده‌ها قابل‌مرتب‌سازی یا مقایسه نیستند. برای مثال، [None, 'hello', 10] مرتب نمی‌شود زیرا اعداد صحیح را نمی‌توان با رشته‌ها مقایسه کرد و None را نمی‌توان با انواع دیگر مقایسه کرد. همچنین، برخی از انواع رابطهٔ ترتیبی تعریف‌شده‌ای ندارند. برای مثال، 3+4j < 5+7j یک مقایسهٔ معتبر نیست.

۵.۱.۱. استفاده از لیست‌ها به‌عنوان پشته (Stack)

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

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

۵.۱.۲. استفاده از لیست‌ها به‌عنوان صف (Queue)

همچنین می‌توان از لیست به‌عنوان یک صف استفاده کرد، جایی که اولین عنصر اضافه شده، اولین عنصری است که بازیابی می‌شود ("اولین ورودی، اولین خروجی" یا FIFO)؛ با این حال، لیست‌ها برای این منظور کارآمد نیستند. در حالی که افزودن (append) و حذف (pop) از انتهای لیست سریع است، انجام درج یا حذف از ابتدای لیست کند است (زیرا تمام عناصر دیگر باید یک موقعیت جابه‌جا شوند).

برای پیاده‌سازی یک صف، از collections.deque استفاده کنید که برای افزودن و حذف سریع از هر دو انتها طراحی شده است. برای مثال:

>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry")           # Terry arrives
>>> queue.append("Graham")          # Graham arrives
>>> queue.popleft()                 # The first to arrive now leaves
'Eric'
>>> queue.popleft()                 # The second to arrive now leaves
'John'
>>> queue                           # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])

۵.۱.۳. درک لیست (List Comprehensions)

درک لیست (List comprehensions) روشی مختصر برای ایجاد لیست‌ها فراهم می‌کند. کاربردهای رایج آن عبارتند از ساخت لیست‌های جدید که در آن هر عنصر نتیجهٔ برخی عملیات اعمال‌شده بر روی هر عضو از یک دنباله یا iterable دیگر است، یا ایجاد یک زیردنباله از آن عناصری که شرایط خاصی را برآورده می‌کنند.

برای مثال، فرض کنید می‌خواهیم یک لیست از مربع‌ها ایجاد کنیم، مانند:

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

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

squares = list(map(lambda x: x**2, range(10)))

یا به‌طور معادل:

squares = [x**2 for x in range(10)]

محاسبه کنیم که مختصرتر و خواناتر است.

یک list comprehension از براکت‌هایی تشکیل شده است که شامل یک عبارت (expression) و به دنبال آن یک عبارت for و سپس صفر یا بیشتر عبارت for یا if است. نتیجه یک لیست جدید خواهد بود که از ارزیابی عبارت در زمینهٔ عبارات for و if که به دنبال آن می‌آیند، حاصل می‌شود. برای مثال، این listcomp عناصر دو لیست را در صورتی که برابر نباشند ترکیب می‌کند:

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

و معادل زیر است:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

توجه کنید که ترتیب عبارات for و if در هر دو قطعه کد یکسان است.

اگر عبارت یک tuple باشد (مانند (x, y) در مثال قبلی)، باید داخل پرانتز قرار گیرد.

>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1
    [x, x**2 for x in range(6)]
     ^^^^^^^
SyntaxError: did you forget parentheses around the comprehension target?
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

List comprehensions می‌توانند شامل عبارات پیچیده و توابع تو در تو (nested) باشند:

>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']

۵.۱.۴. درک لیست تو در تو (Nested List Comprehensions)

عبارت اولیه در یک list comprehension می‌تواند هر عبارت دلخواهی باشد، از جمله یک list comprehension دیگر.

مثال زیر را از یک ماتریس 3x4 در نظر بگیرید که به‌صورت لیستی از ۳ لیست به طول ۴ پیاده‌سازی شده است:

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]

list comprehension زیر سطرها و ستون‌ها را جابه‌جا می‌کند (transpose):

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

همان‌طور که در بخش قبلی دیدیم، list comprehension داخلی در زمینهٔ for که به دنبال آن می‌آید ارزیابی می‌شود، بنابراین این مثال معادل است با:

>>> transposed = []
>>> for i in range(4):
...     transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

که به نوبهٔ خود، همانند زیر است:

>>> transposed = []
>>> for i in range(4):
...     # the following 3 lines implement the nested listcomp
...     transposed_row = []
...     for row in matrix:
...         transposed_row.append(row[i])
...     transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

در دنیای واقعی، باید توابع توکار (built-in functions) را به عبارات جریان کنترل پیچیده ترجیح دهید. تابع zip() کار بسیار خوبی برای این مورد استفاده انجام می‌دهد:

>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

برای جزئیات بیشتر در مورد ستاره (*) در این خط، به بخش Unpacking Argument Lists مراجعه کنید.

۵.۲. عبارت del

راهی برای حذف یک آیتم از لیست با استفاده از اندیس آن به جای مقدارش وجود دارد: عبارت del. این با متد pop() که یک مقدار برمی‌گرداند، تفاوت دارد. عبارت del همچنین می‌تواند برای حذف برش‌هایی (slices) از یک لیست یا پاک کردن کل لیست استفاده شود (که قبلاً با انتساب یک لیست خالی به برش انجام دادیم). برای مثال:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]

del همچنین می‌تواند برای حذف کل متغیرها استفاده شود:

>>> del a

ارجاع به نام a پس از آن خطا خواهد بود (حداقل تا زمانی که مقدار دیگری به آن نسبت داده شود). بعداً کاربردهای دیگری برای del پیدا خواهیم کرد.

۵.۳. تاپل‌ها (Tuples) و دنباله‌ها (Sequences)

دیدیم که لیست‌ها و رشته‌ها ویژگی‌های مشترک زیادی دارند، مانند عملیات ایندکس‌گذاری (indexing) و برش زدن (slicing). آن‌ها دو مثال از انواع دادهٔ دنباله‌ای هستند (به Sequence Types — list, tuple, range مراجعه کنید). از آنجا که پایتون یک زبان در حال تکامل است، ممکن است انواع دادهٔ دنباله‌ای دیگری نیز اضافه شوند. یک نوع دادهٔ دنباله‌ای استاندارد دیگر نیز وجود دارد: تاپل (tuple).

یک تاپل از تعدادی مقدار تشکیل شده است که با کاما از هم جدا شده‌اند، برای مثال:

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
>>> u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
>>> t[0] = 88888
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
>>> v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])

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

اگرچه تاپل‌ها ممکن است شبیه به لیست‌ها به نظر برسند، اما اغلب در موقعیت‌های مختلف و برای اهداف مختلف استفاده می‌شوند. تاپل‌ها غیرقابل‌تغییر (immutable) هستند و معمولاً شامل یک دنبالهٔ ناهمگن (heterogeneous) از عناصر هستند که از طریق unpacking (بعداً در این بخش مشاهده کنید) یا ایندکس‌گذاری (یا حتی از طریق attribute در مورد namedtupleها) به آن‌ها دسترسی پیدا می‌شود. لیست‌ها قابل‌تغییر (mutable) هستند و عناصر آن‌ها معمولاً همگن (homogeneous) هستند و با تکرار روی لیست (iterating) به آن‌ها دسترسی پیدا می‌شود.

یک مشکل خاص، ساخت تاپل‌های حاوی ۰ یا ۱ آیتم است: نحو (syntax) دارای نکات اضافی برای تطابق با این موارد است. تاپل‌های خالی با یک جفت پرانتز خالی ساخته می‌شوند؛ یک تاپل با یک آیتم با دنبال کردن یک مقدار با یک کاما ساخته می‌شود (قرار دادن یک مقدار واحد در پرانتز کافی نیست). زشت است، اما مؤثر. برای مثال:

>>> empty = ()
>>> singleton = 'hello',    # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

عبارت t = 12345, 54321, 'hello!' نمونه‌ای از tuple packing است: مقادیر 12345، 54321 و 'hello!' در یک تاپل بسته‌بندی می‌شوند. عملیات معکوس نیز ممکن است:

>>> x, y, z = t

این عمل به درستی sequence unpacking نامیده می‌شود و برای هر دنباله‌ای در سمت راست کار می‌کند. sequence unpacking نیاز دارد که به تعداد عناصر موجود در دنباله، متغیر در سمت چپ علامت مساوی وجود داشته باشد. توجه داشته باشید که انتساب چندگانه (multiple assignment) در واقع ترکیبی از tuple packing و sequence unpacking است.

۵.۴. مجموعه‌ها (Sets)

پایتون همچنین شامل یک نوع داده برای مجموعه‌ها است. یک مجموعه (set) یک مجموعهٔ نامرتب (unordered collection) بدون عناصر تکراری است. کاربردهای اساسی آن شامل تست عضویت (membership testing) و حذف ورودی‌های تکراری است. اشیاء set همچنین از عملیات ریاضی مانند اتحاد (union)، اشتراک (intersection)، تفاضل (difference)، و تفاضل متقارن (symmetric difference) پشتیبانی می‌کنند.

از آکولاد (پرانتزهای مجعد) یا تابع set() می‌توان برای ایجاد مجموعه‌ها استفاده کرد. توجه: برای ایجاد یک مجموعهٔ خالی باید از set() استفاده کنید، نه {}؛ دومی یک دیکشنری خالی ایجاد می‌کند، که یک ساختار داده است که در بخش بعدی به بحث در مورد آن می‌پردازیم.

از آنجا که مجموعه‌ها نامرتب هستند، تکرار روی آن‌ها یا چاپ آن‌ها ممکن است عناصر را به ترتیبی متفاوت از آنچه انتظار دارید تولید کند.

در اینجا یک نمایش مختصر آورده شده است:

>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # show that duplicates have been removed
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # fast membership testing
True
>>> 'crabgrass' in basket
False
>>> # Demonstrate set operations on unique letters from two words
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # letters in a but not in b
{'r', 'd', 'b'}
>>> a | b                              # letters in a or b or both
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # letters in both a and b
{'a', 'c'}
>>> a ^ b                              # letters in a or b but not both
{'r', 'd', 'b', 'm', 'z', 'l'}

به‌طور مشابه با list comprehensions، set comprehensions نیز پشتیبانی می‌شوند:

>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

۵.۵. دیکشنری‌ها (Dictionaries)

یکی دیگر از انواع دادهٔ مفید ساخته‌شده در پایتون، دیکشنری است (به Mapping Types — dict مراجعه کنید). دیکشنری‌ها گاهی در زبان‌های دیگر به‌عنوان "حافظه‌های انجمنی" (associative memories) یا "آرایه‌های انجمنی" (associative arrays) یافت می‌شوند. برخلاف دنباله‌ها که توسط یک محدوده از اعداد ایندکس می‌شوند، دیکشنری‌ها توسط کلیدها (keys) ایندکس می‌شوند، که می‌توانند هر نوع غیرقابل‌تغییر (immutable) باشند؛ رشته‌ها و اعداد همیشه می‌توانند کلید باشند. تاپل‌ها می‌توانند به‌عنوان کلید استفاده شوند اگر فقط شامل رشته‌ها، اعداد یا تاپل‌ها باشند؛ اگر یک تاپل به‌طور مستقیم یا غیرمستقیم شامل هر شیء قابل‌تغییری باشد، نمی‌توان از آن به‌عنوان کلید استفاده کرد. نمی‌توانید از لیست‌ها به‌عنوان کلید استفاده کنید، زیرا لیست‌ها را می‌توان در همان مکان با استفاده از انتساب ایندکس، انتساب برش، یا متدهایی مانند append() و extend() اصلاح کرد.

بهتر است به یک دیکشنری به‌عنوان یک مجموعه از جفت‌های key: value فکر کنید، با این شرط که کلیدها (در یک دیکشنری) منحصربه‌فرد هستند. یک جفت آکولاد یک دیکشنری خالی ایجاد می‌کند: {}. قرار دادن یک لیست با کاما از جفت‌های key:value در داخل آکولاد، جفت‌های key:value اولیه را به دیکشنری اضافه می‌کند؛ این همچنین روشی است که دیکشنری‌ها در خروجی نوشته می‌شوند.

عملیات اصلی روی یک دیکشنری، ذخیرهٔ یک مقدار با یک کلید و استخراج مقدار با توجه به کلید است. همچنین امکان حذف یک جفت key:value با del وجود دارد. اگر با استفاده از کلیدی که قبلاً استفاده شده است، ذخیره‌سازی انجام دهید، مقدار قدیمی مرتبط با آن کلید فراموش می‌شود.

استخراج مقدار برای یک کلید ناموجود با زیرنویس (subscripting) (d[key]) یک KeyError ایجاد می‌کند. برای جلوگیری از این خطا هنگام تلاش برای دسترسی به یک کلید که احتمالاً وجود ندارد، از متد get() استفاده کنید، که اگر کلید در دیکشنری نباشد، None (یا یک مقدار پیش‌فرض مشخص) را برمی‌گرداند.

اجرای list(d) روی یک دیکشنری، لیستی از تمام کلیدهای استفاده‌شده در دیکشنری را به ترتیب درج (insertion order) برمی‌گرداند (اگر می‌خواهید مرتب شود، فقط از sorted(d) استفاده کنید). برای بررسی اینکه آیا یک کلید واحد در دیکشنری وجود دارد، از کلیدواژهٔ in استفاده کنید.

در اینجا یک مثال کوچک با استفاده از دیکشنری آورده شده است:

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> tel['irv']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'irv'
>>> print(tel.get('irv'))
None
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

سازندهٔ dict() دیکشنری‌ها را به‌طور مستقیم از دنباله‌های جفت‌های کلید-مقدار می‌سازد:

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

علاوه بر این، از dict comprehensions می‌توان برای ایجاد دیکشنری‌ها از عبارات کلید و مقدار دلخواه استفاده کرد:

>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

زمانی که کلیدها رشته‌های ساده هستند، گاهی اوقات مشخص کردن جفت‌ها با استفاده از آرگومان‌های کلیدواژه‌ای (keyword arguments) آسان‌تر است:

>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

۵.۶. تکنیک‌های حلقه (Looping Techniques)

هنگام حلقه زدن روی دیکشنری‌ها، کلید و مقدار متناظر را می‌توان همزمان با استفاده از متد items() بازیابی کرد.

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

هنگام حلقه زدن روی یک دنباله، اندیس موقعیت و مقدار متناظر را می‌توان همزمان با استفاده از تابع enumerate() بازیابی کرد.

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

برای حلقه زدن روی دو یا چند دنباله به‌طور همزمان، می‌توان ورودی‌ها را با تابع zip() جفت کرد.

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

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

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

برای حلقه زدن روی یک دنباله به ترتیب مرتب‌شده، از تابع sorted() استفاده کنید که یک لیست مرتب‌شدهٔ جدید را برمی‌گرداند در حالی که منبع را بدون تغییر نگه می‌دارد.

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for i in sorted(basket):
...     print(i)
...
apple
apple
banana
orange
orange
pear

استفاده از set() روی یک دنباله، عناصر تکراری را حذف می‌کند. استفاده از sorted() در ترکیب با set() روی یک دنباله، روشی اصطلاحاً (idiomatic) برای حلقه زدن روی عناصر منحصربه‌فرد دنباله به ترتیب مرتب‌شده است.

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

گاهی اوقات وسوسه‌انگیز است که یک لیست را در حین حلقه زدن روی آن تغییر دهید؛ با این حال، اغلب ساده‌تر و ایمن‌تر است که به جای آن یک لیست جدید ایجاد کنید.

>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
...     if not math.isnan(value):
...         filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]

۵.۷. بیشتر دربارهٔ شرایط (Conditions)

شرایط استفاده‌شده در دستورات while و if می‌توانند شامل هر عملگری باشند، نه فقط عملگرهای مقایسه‌ای.

عملگرهای مقایسه‌ای in و not in آزمون‌های عضویت (membership tests) هستند که تعیین می‌کنند آیا یک مقدار در یک ظرف (container) وجود دارد (یا وجود ندارد). عملگرهای is و is not مقایسه می‌کنند که آیا دو شیء واقعاً همان شیء هستند یا خیر. همهٔ عملگرهای مقایسه‌ای اولویت یکسانی دارند، که کمتر از اولویت همهٔ عملگرهای عددی است.

مقایسه‌ها می‌توانند زنجیره‌ای شوند. برای مثال، a < b == c بررسی می‌کند که آیا a از b کوچک‌تر است و همچنین b برابر با c است.

مقایسه‌ها ممکن است با استفاده از عملگرهای بولی and و or ترکیب شوند، و نتیجهٔ یک مقایسه (یا هر عبارت بولی دیگر) ممکن است با not نفی شود. این‌ها اولویت کمتری نسبت به عملگرهای مقایسه‌ای دارند؛ بین آن‌ها، not بالاترین اولویت و or کمترین اولویت را دارد، به‌طوری که A and not B or C معادل (A and (not B)) or C است. مانند همیشه، می‌توان از پرانتز برای بیان ترکیب مورد نظر استفاده کرد.

عملگرهای بولی and و or به‌اصطلاح عملگرهای short-circuit هستند: آرگومان‌های آن‌ها از چپ به راست ارزیابی می‌شوند، و به محض اینکه نتیجه مشخص شد، ارزیابی متوقف می‌شود. برای مثال، اگر A و C درست (true) باشند اما B نادرست (false) باشد، A and B and C عبارت C را ارزیابی نمی‌کند. زمانی که به‌عنوان یک مقدار عمومی و نه به‌عنوان یک مقدار بولی استفاده می‌شود، مقدار بازگشتی یک عملگر short-circuit، آخرین آرگومان ارزیابی‌شده است.

امکان انتساب نتیجهٔ یک مقایسه یا عبارت بولی دیگر به یک متغیر وجود دارد. برای مثال،

>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

توجه داشته باشید که در پایتون، برخلاف C، انتساب درون عبارات باید به‌صراحت با عملگر walrus := انجام شود. این کار از یک دستهٔ رایج از مشکلاتی که در برنامه‌های C رخ می‌دهد جلوگیری می‌کند: تایپ = در یک عبارت زمانی که == مورد نظر بوده است.

۵.۸. مقایسهٔ دنباله‌ها (Sequences) و انواع دیگر

اشیاء دنباله‌ای معمولاً ممکن است با اشیاء دیگر با همان نوع دنباله مقایسه شوند. مقایسه از ترتیب فرهنگ‌لغتی (lexicographical ordering) استفاده می‌کند: ابتدا دو آیتم اول مقایسه می‌شوند، و اگر متفاوت باشند، این نتیجهٔ مقایسه را تعیین می‌کند؛ اگر برابر باشند، دو آیتم بعدی مقایسه می‌شوند، و به همین ترتیب، تا زمانی که یکی از دنباله‌ها به پایان برسد. اگر دو آیتمی که باید مقایسه شوند خود دنباله‌هایی از همان نوع باشند، مقایسهٔ فرهنگ‌لغتی به‌صورت بازگشتی (recursively) انجام می‌شود. اگر تمام آیتم‌های دو دنباله برابر باشند، دنباله‌ها برابر در نظر گرفته می‌شوند. اگر یک دنباله زیردنبالهٔ ابتدایی (initial sub-sequence) دیگری باشد، دنبالهٔ کوتاه‌تر کوچک‌تر (کمتر) است. ترتیب فرهنگ‌لغتی برای رشته‌ها از شمارهٔ نقطه کد یونیکد (Unicode code point number) برای مرتب کردن کاراکترهای منفرد استفاده می‌کند. چند نمونه از مقایسه بین دنباله‌های یک نوع:

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

توجه داشته باشید که مقایسهٔ اشیاء از انواع مختلف با < یا > به شرطی مجاز است که اشیاء دارای متدهای مقایسهٔ مناسب باشند. برای مثال، انواع عددی مختلط با توجه به مقدار عددی آن‌ها مقایسه می‌شوند، بنابراین 0 برابر با 0.0 است و غیره. در غیر این صورت، به جای ارائهٔ یک ترتیب دلخواه، مفسر یک استثنای TypeError ایجاد می‌کند.

پانویس‌ها

[1] زبان‌های دیگر ممکن است شیء تغییر یافته را برگردانند، که امکان زنجیره‌ای کردن متدها را فراهم می‌کند، مانند d->insert("a")->remove("b")->sort();.