منابع ترجمه · The Python Tutorial · فصل ۵
ساختارهای داده (آموزش رسمی پایتون، فصل ۵)
منبع اصلی: The Python Tutorial, Chapter 5: Data Structures — Python Software Foundation
💡 این صفحه مثل یک نوتبوک Jupyter است: متغیر و تابعی که در یک سلول تعریف شود، در سلولهای بعدی همین صفحه در دسترس است. سلولها را به ترتیب اجرا کنید، یا با «⏮ تا اینجا» همهٔ سلولهای قبلیِ اجرانشده را یکجا اجرا کنید.
این فصل برخی از چیزهایی را که قبلاً یاد گرفتهاید با جزئیات بیشتر توضیح میدهد و چیزهای جدیدی هم اضافه میکند.
۵٫۱. بیشتر دربارهٔ فهرستها
نوع دادهٔ فهرست (list) متدهای بیشتری دارد. در اینجا همهٔ متدهای اشیای فهرست آمده است:
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 مانند نمادگذاری برش تفسیر میشوند و برای محدود کردن جستوجو به یک زیردنبالهٔ خاص از فهرست به کار میروند. اندیس بازگرداندهشده نسبت به ابتدای کل دنباله محاسبه میشود، نه نسبت به آرگومان start.
list.count(value, /)
: تعداد دفعاتی را که value در فهرست ظاهر میشود برمیگرداند.
list.sort(*, key=None, reverse=False)
: آیتمهای فهرست را درجا (in place) مرتب میکند (از آرگومانها میتوان برای سفارشیسازی مرتبسازی استفاده کرد؛ برای توضیح آنها به sorted() نگاه کنید).
list.reverse()
: عناصر فهرست را درجا معکوس میکند.
list.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 پیشفرض را برمیگردانند. [۱] این یک اصل طراحی برای همهٔ ساختارهای دادهٔ تغییرپذیر در پایتون است.
نکتهٔ دیگری که ممکن است متوجه شوید این است که همهٔ دادهها را نمیتوان مرتب یا مقایسه کرد. برای مثال، [None, 'hello', 10] مرتب نمیشود چون اعداد صحیح را نمیتوان با رشتهها مقایسه کرد و None را نمیتوان با انواع دیگر مقایسه کرد. همچنین انواعی وجود دارند که رابطهٔ ترتیب تعریفشده ندارند. برای مثال، 3+4j < 5+7j یک مقایسهٔ معتبر نیست.
۵٫۱٫۱. استفاده از فهرستها بهعنوان پشته
متدهای فهرست استفاده از یک فهرست را بهعنوان پشته (stack) بسیار آسان میکنند، که در آن آخرین عنصر اضافهشده اولین عنصری است که بازیابی میشود («آخرین ورودی، اولین خروجی»). برای افزودن یک آیتم به بالای پشته از 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) استفاده کرد، که در آن اولین عنصر اضافهشده اولین عنصری است که بازیابی میشود («اولین ورودی، اولین خروجی»)؛ با این حال، فهرستها برای این منظور کارآمد نیستند. در حالی که افزودن و برداشتن از انتهای فهرست سریع است، درج یا برداشتن از ابتدای فهرست کند است (چون همهٔ عناصر دیگر باید یک خانه جابهجا شوند). برای اطلاعات بیشتر به پیچیدگی زمانی مراجعه کنید.
برای پیادهسازی یک صف، از 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)
لیست کامپرهنشنها راهی موجز برای ساختن فهرستها فراهم میکنند. کاربردهای رایج، ساختن فهرستهای جدیدی است که هر عنصر آن نتیجهٔ برخی عملیات اعمالشده روی هر عضو یک دنباله یا تکرارپذیر دیگر است، یا ساختن زیردنبالهای از عناصری که شرط خاصی را برآورده میکنند.
برای مثال، فرض کنید میخواهیم فهرستی از مربعها بسازیم، مثل:
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
توجه کنید که این کار یک متغیر به نام x میسازد (یا بازنویسی میکند) که پس از پایان حلقه هنوز وجود دارد. میتوانیم فهرست مربعها را بدون هیچ عارضهٔ جانبی با این روش محاسبه کنیم:
squares = list(map(lambda x: x**2, range(10)))
یا بهطور همارز:
squares = [x**2 for x in range(10)]
که موجزتر و خواناتر است.
یک لیست کامپرهنشن شامل کروشههایی است که یک عبارت را در بر دارند و به دنبال آن یک بند 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 در هر دو قطعه یکسان است.
اگر عبارت یک تاپل باشد (مثلاً (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]
لیست کامپرهنشنها میتوانند عبارتهای پیچیده و توابع تودرتو داشته باشند:
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
۵٫۱٫۴. لیست کامپرهنشنهای تودرتو
عبارت اولیهٔ یک لیست کامپرهنشن میتواند هر عبارت دلخواهی باشد، از جمله یک لیست کامپرهنشن دیگر.
مثال زیر را از یک ماتریس ۳×۴ در نظر بگیرید که بهصورت فهرستی از ۳ فهرست به طول ۴ پیادهسازی شده است:
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
لیست کامپرهنشن زیر سطرها و ستونها را جابهجا (transpose) میکند:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
همانطور که در بخش قبل دیدیم، لیست کامپرهنشن درونی در زمینهٔ 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]]
در دنیای واقعی، باید توابع درونساخت را بر دستورهای جریان پیچیده ترجیح دهید. تابع zip() برای این مورد کار عالیای انجام میدهد:
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
برای جزئیات دربارهٔ ستاره در این خط به باز کردن بستهٔ فهرستهای آرگومان نگاه کنید.
۵٫۱٫۵. باز کردن بسته در فهرستها و لیست کامپرهنشنها
بخش باز کردن بستهٔ فهرستهای آرگومان استفاده از * را برای «باز کردن بستهٔ» عناصر یک شیء تکرارپذیر توصیف میکند، بهطوری که هر کدام جداگانه بهعنوان آرگومان به یک تابع داده شوند. باز کردن بسته را میتوان در زمینههای دیگر هم به کار برد، مثلاً هنگام ساختن فهرستها. هنگام مشخص کردن عناصر یک فهرست، گذاشتن * پیش از یک عبارت، نتیجهٔ آن عبارت را باز میکند و هر یک از عناصرش را به فهرستی که میسازیم اضافه میکند:
>>> x = [1, 2, 3]
>>> [0, *x, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6]
این فقط وقتی کار میکند که عبارتِ بعد از * به یک شیء تکرارپذیر ارزیابی شود؛ تلاش برای باز کردن بستهٔ یک شیء غیرتکرارپذیر یک استثنا پرتاب میکند:
>>> x = 1
>>> [0, *x, 2, 3, 4]
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
[0, *x, 2, 3, 4]
TypeError: Value after * must be an iterable, not int
باز کردن بسته را میتوان در لیست کامپرهنشنها هم به کار برد، بهعنوان راهی برای ساختن فهرست جدیدی که نمایندهٔ اتصال تعداد دلخواهی تکرارپذیر است:
>>> x = [[1, 2, 3], [4, 5, 6], [], [7], [8, 9]]
>>> [*element for element in x]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
توجه کنید که اثر آن این است که هر عنصر از x باز میشود. این برای اشیای تکرارپذیر دلخواه کار میکند، نه فقط فهرستها:
>>> x = [[1, 2, 3], 'cat', {'spam': 'eggs'}]
>>> [*element for element in x]
[1, 2, 3, 'c', 'a', 't', 'spam']
اما اگر اشیای موجود در x تکرارپذیر نباشند، این عبارت دوباره یک استثنا پرتاب میکند.
۵٫۲. دستور del
راهی برای حذف یک آیتم از فهرست با داشتن اندیس آن بهجای مقدارش وجود دارد: دستور del. این با متد pop() که یک مقدار برمیگرداند فرق دارد. دستور del را همچنین میتوان برای حذف برشها از یک فهرست یا خالی کردن کل فهرست (که قبلاً با انتساب یک فهرست خالی به برش انجام دادیم) به کار برد. برای مثال:
>>> 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 خواهیم یافت.
۵٫۳. تاپلها و دنبالهها
دیدیم که فهرستها و رشتهها ویژگیهای مشترک زیادی دارند، مانند عملیات اندیسگذاری و برش. اینها دو نمونه از انواع دادهٔ دنباله (sequence) هستند (انواع دنباله را ببینید). از آنجا که پایتون زبانی در حال تکامل است، ممکن است انواع دادهٔ دنبالهٔ دیگری اضافه شود. نوع دادهٔ دنبالهٔ استاندارد دیگری هم وجود دارد: تاپل (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])
>>> # they support unpacking just like lists:
>>> x = [1, 2, 3]
>>> 0, *x, 4
(0, 1, 2, 3, 4)
همانطور که میبینید، در خروجی تاپلها همیشه داخل پرانتز قرار میگیرند، تا تاپلهای تودرتو درست تفسیر شوند؛ ورود آنها ممکن است با یا بدون پرانتز دور آنها باشد، هرچند اغلب به هر حال پرانتز لازم است (اگر تاپل بخشی از یک عبارت بزرگتر باشد). نمیتوان به آیتمهای منفرد یک تاپل مقدار نسبت داد، اما میتوان تاپلهایی ساخت که شامل اشیای تغییرپذیر مانند فهرستها باشند.
هرچند تاپلها ممکن است شبیه فهرستها به نظر برسند، اغلب در موقعیتهای متفاوت و برای مقاصد متفاوتی به کار میروند. تاپلها تغییرناپذیر هستند و معمولاً شامل دنبالهای ناهمگن از عناصرند که از طریق باز کردن بسته (بعداً در همین بخش را ببینید) یا اندیسگذاری (یا حتی با ویژگی در مورد namedtuples) دسترسی مییابند. فهرستها تغییرپذیر هستند و عناصرشان معمولاً همگن است و با تکرار روی فهرست دسترسی مییابند.
یک مشکل خاص ساختن تاپلهایی با ۰ یا ۱ آیتم است: نحو برای سازگاری با این موارد چند ویژگی عجیب اضافی دارد. تاپلهای خالی با یک جفت پرانتز خالی ساخته میشوند؛ تاپل با یک آیتم با گذاشتن ویرگول بعد از یک مقدار ساخته میشود (کافی نیست که یک مقدار تنها را داخل پرانتز قرار دهید). زشت است، اما مؤثر. برای مثال:
>>> 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) نام دارد و برای هر دنبالهای در سمت راست کار میکند. باز کردن بستهٔ دنباله لازم دارد که تعداد متغیرهای سمت چپ علامت مساوی با تعداد عناصر دنباله برابر باشد. توجه کنید که انتساب چندگانه در واقع فقط ترکیبی از بستهبندی تاپل و باز کردن بستهٔ دنباله است.
۵٫۴. مجموعهها
پایتون همچنین یک نوع دادهٔ مجموعه (set) دارد. مجموعه یک گردآوری بدون ترتیب و بدون عناصر تکراری است. کاربردهای پایه شامل آزمون عضویت و حذف ورودیهای تکراری است. اشیای مجموعه همچنین عملیات ریاضی مانند اجتماع، اشتراک، تفاضل و تفاضل متقارن را پشتیبانی میکنند.
برای ساختن مجموعهها میتوان از آکولادها یا تابع 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'}
مشابه لیست کامپرهنشنها، کامپرهنشنهای مجموعه هم پشتیبانی میشوند، از جمله کامپرهنشنها با باز کردن بسته:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
>>> fruits = [{'apple', 'avocado', 'apricot'}, {'banana', 'blueberry'}]
>>> {*fruit for fruit in fruits}
{'blueberry', 'banana', 'avocado', 'apple', 'apricot'}
۵٫۵. دیکشنریها
نوع دادهٔ مفید دیگری که در پایتون درونساخت است دیکشنری (dictionary) است (انواع نگاشت را ببینید). دیکشنریها گاهی در زبانهای دیگر بهصورت «حافظههای انجمنی» (associative memories) یا «آرایههای انجمنی» (associative arrays) یافت میشوند. بر خلاف دنبالهها که با یک بازهٔ اعداد اندیسگذاری میشوند، دیکشنریها با کلیدها اندیسگذاری میشوند که میتوانند هر نوع تغییرناپذیری باشند؛ رشتهها و اعداد همیشه میتوانند کلید باشند. تاپلها را وقتی میتوان بهعنوان کلید به کار برد که فقط شامل رشتهها، اعداد یا تاپلها باشند؛ اگر تاپلی بهطور مستقیم یا غیرمستقیم شامل هر شیء تغییرپذیری باشد، نمیتوان از آن بهعنوان کلید استفاده کرد. نمیتوانید از فهرستها بهعنوان کلید استفاده کنید، چون فهرستها را میتوان با انتسابهای اندیس، انتسابهای برش یا متدهایی مانند append() و extend() درجا تغییر داد.
بهتر است به دیکشنری بهعنوان مجموعهای از جفتهای کلید: مقدار فکر کنید، با این الزام که کلیدها (درون یک دیکشنری) یکتا باشند. یک جفت آکولاد یک دیکشنری خالی میسازد: {}. قرار دادن فهرستی از جفتهای کلید:مقدار جداشده با ویرگول درون آکولادها، جفتهای کلید:مقدار اولیه را به دیکشنری اضافه میکند؛ دیکشنریها در خروجی هم به همین شکل نوشته میشوند.
عملیات اصلی روی یک دیکشنری ذخیرهٔ یک مقدار با یک کلید و استخراج مقدار با داشتن کلید است. همچنین میتوان یک جفت کلید:مقدار را با del حذف کرد. اگر با کلیدی که از قبل در حال استفاده است ذخیره کنید، مقدار قدیمی مرتبط با آن کلید فراموش میشود.
استخراج مقدار برای یک کلید ناموجود با اندیسگذاری (d[key]) یک KeyError پرتاب میکند. برای جلوگیری از دریافت این خطا هنگام تلاش برای دسترسی به کلیدی که ممکن است وجود نداشته باشد، بهجای آن از متد get() استفاده کنید که اگر کلید در دیکشنری نباشد None (یا یک مقدار پیشفرض مشخصشده) برمیگرداند.
اجرای list(d) روی یک دیکشنری فهرستی از همهٔ کلیدهای استفادهشده در دیکشنری را به ترتیب درج برمیگرداند (اگر میخواهید مرتبشده باشد، بهجای آن از 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 comprehensionها برای ساختن دیکشنریها از عبارتهای دلخواه کلید و مقدار استفاده کرد:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
و باز کردن بستهٔ دیکشنری (با **) را میتوان برای ادغام چند دیکشنری به کار برد:
>>> odds = {i: i**2 for i in (1, 3, 5)}
>>> evens = {i: i**2 for i in (2, 4, 6)}
>>> {**odds, **evens}
{1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36}
>>> all_values = [odds, evens, {0: 0}]
>>> {**i for i in all_values}
{1: 1, 3: 9, 5: 25, 2: 4, 4: 16, 6: 36, 0: 0}
وقتی کلیدها رشتههای ساده هستند، گاهی مشخص کردن جفتها با آرگومانهای کلیدواژهای آسانتر است:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
۵٫۶. تکنیکهای حلقهزدن
هنگام حلقهزدن روی دیکشنریها، میتوان کلید و مقدار متناظر را همزمان با متد 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() روی یک دنباله، روشی رایج برای حلقهزدن روی عناصر یکتای دنباله بهصورت مرتبشده است.
>>> 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]
۵٫۷. بیشتر دربارهٔ شرطها
شرطهایی که در دستورهای while و if به کار میروند میتوانند شامل هر عملگری باشند، نه فقط مقایسهها.
عملگرهای مقایسهٔ in و not in آزمونهای عضویت هستند که تعیین میکنند یک مقدار در یک ظرف (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 درست باشند اما B نادرست، A and B and C عبارت C را ارزیابی نمیکند. وقتی بهعنوان یک مقدار عمومی و نه بهعنوان بولی به کار روند، مقدار بازگشتی یک عملگر اتصال کوتاه آخرین آرگومان ارزیابیشده است.
میتوان نتیجهٔ یک مقایسه یا عبارت بولی دیگر را به یک متغیر نسبت داد. برای مثال،
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
توجه کنید که در پایتون، بر خلاف C، انتساب درون عبارتها باید بهصراحت با عملگر والروس (walrus) := انجام شود. این از یک دستهٔ رایج مشکلاتی که در برنامههای C با آن روبهرو میشوند جلوگیری میکند: تایپ = در یک عبارت وقتی منظور == بوده است.
۵٫۸. مقایسهٔ دنبالهها و انواع دیگر
اشیای دنباله معمولاً میتوانند با اشیای دیگر که نوع دنبالهٔ یکسانی دارند مقایسه شوند. مقایسه از ترتیب لغتنامهای (lexicographical) استفاده میکند: ابتدا دو آیتم اول مقایسه میشوند، و اگر متفاوت باشند، این نتیجهٔ مقایسه را تعیین میکند؛ اگر برابر باشند، دو آیتم بعدی مقایسه میشوند و به همین ترتیب، تا زمانی که هر یک از دنبالهها تمام شود. اگر دو آیتمی که باید مقایسه شوند خودشان دنبالههایی از یک نوع باشند، مقایسهٔ لغتنامهای بهصورت بازگشتی انجام میشود. اگر همهٔ آیتمهای دو دنباله برابر مقایسه شوند، دنبالهها برابر در نظر گرفته میشوند. اگر یک دنباله زیردنبالهٔ آغازین دیگری باشد، دنبالهٔ کوتاهتر کوچکتر (کمتر) است. ترتیب لغتنامهای برای رشتهها از شمارهٔ نقطهٔ کد یونیکد (Unicode code point) برای مرتب کردن نویسههای منفرد استفاده میکند. چند نمونه از مقایسه بین دنبالههای همنوع:
(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 پرتاب میکند.
پاورقیها
[۱] زبانهای دیگر ممکن است شیء تغییریافته را برگردانند، که امکان زنجیرهای کردن متدها را میدهد، مانند d->insert("a")->remove("b")->sort();.