مسیر
/
Python
Python
/
برنامه‌ی درسی
/
بازگشایی و انتساب چندگانه
با

بازگشایی و انتساب چندگانه در Python

3 تمرین

درباره‌ی بازگشایی و انتساب چندگانه

«بازگشایی» به عمل استخراج عناصر یک مجموعه، مانند list، tuple یا dict، با استفاده از تکرار اشاره دارد. سپس می‌توان مقادیر بازگشایی‌شده را در همان دستور به متغیرها انتساب داد. یک مثال بسیار رایج از این رفتار for item in list است، که در آن item در طول تکرار به نوبت مقدار هر عنصر list را می‌گیرد.

انتساب چندگانه توانایی انتساب چند متغیر به مقادیر بازگشایی‌شده در یک دستور است. این کار باعث می‌شود کد مختصرتر و خواناتر باشد و با جدا کردن متغیرهایی که باید انتساب داده شوند با یک کاما انجام می‌شود، مانند first, second, third = (1,2,3) یا for index, item in enumerate(iterable).

عملگرهای ویژه‌ی * و ** اغلب در بافت‌های بازگشایی استفاده می‌شوند. می‌توان از * برای ترکیب چند list/tuple در یک list/tuple استفاده کرد، با بازگشایی هر کدام در یک list/tuple مشترک جدید. می‌توان از ** برای ترکیب چند دیکشنری در یک دیکشنری استفاده کرد، با بازگشایی هر کدام در یک dict مشترک جدید.

وقتی عملگر * بدون یک مجموعه استفاده می‌شود، تعدادی مقدار را در یک list بسته‌بندی می‌کند. این کار اغلب در انتساب چندگانه به کار می‌رود تا همه‌ی عناصر «باقی‌مانده» را که انتساب جداگانه ندارند، در یک متغیر واحد گروه‌بندی کند.

در Python رایج است که هنگام استفاده یا تعریف توابعی که تعداد دلخواهی آرگومان مکانی یا کلیدواژه‌ای می‌گیرند، از این رفتار بازگشایی/بسته‌بندی نیز بهره برده شود. اغلب این پارامترهای «ویژه» را به‌صورت def some_function(*args, **kwargs) تعریف‌شده می‌بینید و آرگومان‌های «ویژه» را به‌صورت some_function(*some_tuple, **some_dict) به کار می‌برید.

Caution

نباید *<variable_name> و **<variable_name> را با * و ** اشتباه گرفت. در حالی که * و ** به‌ترتیب برای ضرب و توان استفاده می‌شوند، *<variable_name> و **<variable_name> به‌عنوان عملگرهای بسته‌بندی و بازگشایی به کار می‌روند.

انتساب چندگانه

در انتساب چندگانه، تعداد متغیرهای سمت چپ عملگر انتساب (=) باید با تعداد مقادیر سمت راست برابر باشد. برای جدا کردن مقادیر، از کاما , استفاده کنید:

>>> a, b = 1, 2
>>> a
1

اگر انتساب چندگانه تعداد نادرستی از متغیرها برای مقادیر داده‌شده دریافت کند، یک ValueError پرتاب می‌شود:

>>> x, y, z = 1, 2

ValueError: too many values to unpack (expected 3, got 2)

انتساب چندگانه به یک نوع داده محدود نمی‌شود:

>>> x, y, z = 1, "Hello", True
>>> x
1

>>> y
'Hello'

>>> z
True

می‌توان از انتساب چندگانه برای جابه‌جایی عناصر در lists استفاده کرد. این کار در الگوریتم‌های مرتب‌سازی بسیار رایج است. برای مثال:

>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]

از آنجا که tuples تغییرناپذیرند، نمی‌توانید عناصر یک tuple را جابه‌جا کنید.

بازگشایی

Note

مثال‌های زیر از lists استفاده می‌کنند، اما مفاهیم یکسان برای tuples هم کاربرد دارند.

در Python می‌توان عناصر یک list/tuple/dict را در متغیرهای جداگانه بازگشایی کرد. از آنجا که مقادیر در lists/tuples با ترتیب مشخصی ظاهر می‌شوند، به همان ترتیب در متغیرها بازگشایی می‌شوند:

>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"

اگر مقادیری باشند که به آن‌ها نیازی نیست، می‌توانید با _ علامت‌شان بزنید:

>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"

بازگشایی عمیق

بازگشایی و انتساب مقادیر از یک list/tuple درون یک list یا tuple (که با نام لیست‌ها/تاپل‌های تودرتو نیز شناخته می‌شود) به همان روش بازگشایی سطحی کار می‌کند، اما اغلب به مشخص‌کننده‌هایی نیاز دارد تا بافت یا موقعیت مقدار را روشن کند:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"

>>> d
"potato"

همچنین می‌توانید فقط بخشی از یک list/tuple تودرتو را به‌صورت عمیق بازگشایی کنید:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]

>>> c
"carrot"

اگر در بازگشایی، متغیرها جای نادرست و/یا تعداد نادرستی از مقادیر داشته باشند، یک ValueError دریافت می‌کنید:

>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables

ValueError: too many values to unpack (expected 1)

بازگشایی یک لیست/تاپل با *

هنگام بازگشایی یک list/tuple می‌توانید از عملگر * برای گرفتن مقادیر «باقی‌مانده» استفاده کنید. این روش از برش دادن list/tuple روشن‌تر است (که در برخی موقعیت‌ها خواناتر نیست). برای مثال، می‌توانیم اولین عنصر را استخراج کنیم و مقادیر باقی‌مانده را بدون اولین عنصر در یک list جدید بسته‌بندی کنیم:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"

>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]

همچنین می‌توانیم مقادیر ابتدا و انتهای list را استخراج کنیم و در همان حال همه‌ی مقادیر میانی را گروه‌بندی کنیم:

>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"

>>> middle
["banana", "cherry", "orange", "kiwi"]

می‌توانیم از * در بازگشایی عمیق هم استفاده کنیم:

>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"

>>> rest
["banana", "melon"]

بازگشایی یک دیکشنری

بازگشایی یک دیکشنری کمی با بازگشایی یک list/tuple تفاوت دارد. تکرار روی دیکشنری‌ها به‌طور پیش‌فرض روی کلیدها انجام می‌شود. بنابراین هنگام بازگشایی یک dict، فقط می‌توانید کلیدها را بازگشایی کنید، نه مقادیر:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"

اگر می‌خواهید مقادیر را بازگشایی کنید، می‌توانید از متد <dict>.values() استفاده کنید:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6

اگر به کلیدها و مقادیر هر دو نیاز دارید، از متد <dict>.items() استفاده کنید. <dict>.items() یک نمای قابل‌تکرار تولید می‌کند که شامل جفت‌های کلید-مقدار است. این‌ها را می‌توان در یک tuple بازگشایی کرد:

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)

بسته‌بندی

«بسته‌بندی» توانایی گروه‌بندی چند مقدار در یک list است که به یک متغیر انتساب داده می‌شود. این کار زمانی مفید است که می‌خواهید مقادیری را بازگشایی کنید، تغییراتی بدهید و سپس نتایج را دوباره در یک متغیر بسته‌بندی کنید. همچنین امکان انجام ادغام روی دو یا چند lists/tuples/dicts را فراهم می‌کند.

بسته‌بندی یک لیست/تاپل با *

بسته‌بندی یک list/tuple را می‌توان با عملگر * انجام داد. این کار همه‌ی مقادیر را در یک list/tuple بسته‌بندی می‌کند.

>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits

# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")

# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma. 
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

# A list literal can be used instead, but might not be as readable.
>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']

برای جزئیات بیشتر درباره‌ی کاربرد * و **، PEP 3132 و PEP 448 را ببینید.

بسته‌بندی یک دیکشنری با **

بسته‌بندی یک دیکشنری با استفاده از عملگر ** انجام می‌شود. این کار همه‌ی جفت‌های کلید-مقدار یک دیکشنری را در دیکشنری دیگری بسته‌بندی می‌کند، یا دو دیکشنری را با هم ترکیب می‌کند.

>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}

کاربرد * و ** همراه با توابع

بسته‌بندی با پارامترهای تابع

وقتی تابعی می‌سازید که تعداد دلخواهی آرگومان می‌پذیرد، می‌توانید در تعریف تابع از *args یا **kwargs استفاده کنید. *args برای بسته‌بندی تعداد دلخواهی آرگومان مکانی (غیرکلیدواژه‌ای) و **kwargs برای بسته‌بندی تعداد دلخواهی آرگومان کلیدواژه‌ای به کار می‌رود.

کاربرد *args:

# This function is defined to take any number of positional arguments

>>> def my_function(*args):
...   print(args)

# Arguments given to the function are packed into a tuple

>>> my_function(1, 2, 3)
(1, 2, 3)

>>> my_function("Hello")
("Hello")

>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")

کاربرد **kwargs:

# This function is defined to take any number of keyword arguments

>>> def my_function(**kwargs):
...   print(kwargs)

# Arguments given to the function are packed into a dictionary

>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}

*args و **kwargs را می‌توان با یکدیگر نیز ترکیب کرد:

>>> def my_function(*args, **kwargs):
...   print(sum(args))
...   for key, value in kwargs.items():
...       print(str(key) + " = " + str(value))

>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3

همچنین می‌توانید پارامترهایی پیش از *args بنویسید تا آرگومان‌های مکانی مشخصی مجاز باشند. سپس آرگومان‌های کلیدواژه‌ای تک‌تک باید پیش از **kwargs بیایند.

Caution

آرگومان‌ها باید به این شکل ساختاربندی شوند:

def my_function(<positional_args>, *args, <key-word_args>, **kwargs)

اگر این ترتیب را رعایت نکنید، خطا دریافت خواهید کرد.

>>> def my_function(a, b, *args):
...   print(a)
...   print(b)
...   print(args)

>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)

نوشتن آرگومان‌ها با ترتیب نادرست منجر به خطا می‌شود:

>>> def my_function(*args, a, b):
...   print(args)

>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
  File "c:\something.py", line 3, in <module>
    my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'

بازگشایی در فراخوانی توابع

می‌توانید از * برای بازگشایی یک list/tuple از آرگومان‌ها در یک فراخوانی تابع استفاده کنید. این کار برای توابعی که یک iterable نمی‌پذیرند بسیار مفید است:

>>> def my_function(a, b, c):
...   print(c)
...   print(b)
...   print(a)

numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1

استفاده از بازگشایی * همراه با تابع درونی zip() مورد کاربرد رایج دیگری است. تابع zip() چند شیء قابل‌تکرار می‌گیرد و یک list از tuples را برمی‌گرداند که مقادیر هر iterable در آن گروه‌بندی شده‌اند:

>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]
ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

بازگشایی و انتساب چندگانه را یاد بگیرید

تمرین کردن قفل شده است

برای تمرین بازگشایی و انتساب چندگانه قفل 3 تمرین دیگر را باز کنید