Як використовувати оператори розпакування (*, **) у Python?

Python є найбільш використовуваною мовою програмування. Сьогодні ви навчитеся використовувати одну з його основних — але часто ігнорованих — функцій, розпаковувати в Python.

Ви, напевно, бачили * і ** в чужому коді або навіть використовували їх, насправді не знаючи, для чого вони призначені. Ми розповімо про концепцію розпакування та про те, як її використовувати для написання додаткового коду Pythonic.

Ось список понять, які будуть корисними для вас під час читання цього підручника:

  • Ітерація: будь-яка послідовність, яку можна ітерувати за допомогою циклу for, наприклад набори, списки, кортежі та словники
  • Викликається: об’єкт Python, який можна викликати за допомогою подвійних дужок (), наприклад, myfunction()
  • Оболонка: інтерактивне середовище виконання, яке дозволяє запускати код Python. Ми можемо викликати це, запустивши «python» у терміналі
  • Змінна: символічне ім’я, яке зберігає об’єкт і має зарезервовану область пам’яті.

Почнемо з найпоширенішої плутанини: зірочки в Python також є арифметичними операторами. Одна зірочка

використовується для множення, тоді як два з них (**) стосуються піднесення до степеня.

>>> 3*3
9
>>> 3**3
27

Ми можемо це довести, відкривши оболонку Python і ввівши:

Примітка: вам потрібно встановити Python 3, щоб слідувати цьому посібнику. Якщо у вас його не встановлено, перегляньте наш посібник зі встановлення Python.

Як бачите, ми використовуємо зірочку після першої цифри та перед другою. Коли ви бачите це, це означає, що ми використовуємо арифметичні оператори.

>>> *range(1, 6),
(1, 2, 3, 4, 5)
>>> {**{'vanilla':3, 'chocolate':2}, 'strawberry':2}
{'vanilla': 3, 'chocolate': 2, 'strawberry': 2}

З іншого боку, ми використовуємо зірочки (*, **) перед iterable, щоб розпакувати його — наприклад:

Не хвилюйтеся, якщо ви цього не зрозуміли, це лише вступ до розпакування в Python. Тож прочитайте весь підручник!

Що розпаковується?

Розпакування — це процес отримання матеріалів — ітерацій, таких як списки, кортежі та словники. Подумайте про це як про відкриття коробки та діставання різних предметів, таких як кабелі, навушники або USB.

Розпакування в Python схоже на розпакування коробки в реальному житті.

>>> mybox = ['cables', 'headphones', 'USB']
>>> item1, item2, item3 = mybox

Давайте переведемо цей приклад у код для кращого розуміння:

  Як перейменувати папки на iPhone або iPad

Як бачите, ми призначаємо три елементи всередині списку mybox трьом змінним item1, item2, item2. Таке призначення змінних є фундаментальною концепцією розпакування в Python.

>>> item1
'cables'
>>> item2
'headphones'
>>> item3
'USB'

Якщо ви спробуєте отримати значення кожного елемента, ви помітите, що елемент 1 відноситься до «кабелів», елемент 2 відноситься до «навушники» і так далі.

>>> newbox = ['cables', 'headphones', 'USB', 'mouse']
>>> item1, item2, item3 = newbox
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

До цього моменту з цим кодом, здається, все гаразд, але що, якби ми захотіли розпакувати список із більшою кількістю елементів у ньому, зберігаючи ту саму кількість призначених змінних?

Можливо, ви очікували такої помилки. По суті, ми призначаємо 4 елементи списку трьом змінним, як Python вдається призначати правильні значення? Це не так, це тому, що ми отримуємо a ValueError

з повідомленням «забагато значень для розпакування». Це відбувається тому, що ми встановлюємо три змінні ліворуч і чотири значення (що відповідають новому списку) праворуч.

>>> lastbox = ['cables', 'headphones']
>>> item1, item2, item3 = lastbox
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)

Якщо ви спробуєте виконати подібний процес, але з більшою кількістю змінних, ніж значень, які потрібно розпакувати, ви отримаєте іншу помилку ValueError, але з дещо іншим повідомленням:

Примітка: ми працювали зі списками, але ви можете використовувати цю форму розпакування з будь-яким ітерованим (списками, наборами, кортежами, словниками)

Отже, як нам подолати цю ситуацію? Чи є спосіб розпакувати всі елементи iterable до кількох змінних, не отримуючи жодних помилок?

Звичайно, є, і він називається оператором розпакування або оператором зірочки (*, **). Давайте подивимося, як це використовувати в Python.

Як розпакувати списки за допомогою оператора *

Оператор зірочка

>>> first, *unused, last = [1, 2, 3, 5, 7]
>>> first
1
>>> last
7
>>> unused
[2, 3, 5]

використовується для розпакування всіх значень iterable, які ще не були призначені.

>>> first, *_, last = [1, 2, 3, 5, 7]
>>> _
[2, 3, 5]

Припустімо, ви хочете отримати перший і останній елемент списку без використання індексів, ми можемо зробити це за допомогою оператора зірочки:

>>> first, *_, last = [1, 2]
>>> first
1
>>> last
2
>>> _
[]

Як ви розумієте, ми отримуємо всі невикористані значення за допомогою оператора зірочки. Кращим способом відхилення значень є використання змінної підкреслення (_), яка іноді використовується як «фіктивна змінна».

Ми можемо використовувати цей трюк, навіть якщо список містить лише два елементи:

У цьому випадку змінна підкреслення (фіктивна змінна) зберігає порожній список, щоб інші дві змінні навколо них могли отримати доступ до доступних значень списку.

>>> *string = 'PythonIsTheBest'

Загальні способи усунення несправностей

>>> *string = 'PythonIsTheBest'
  File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple

Ми можемо розпакувати унікальний елемент iterable. Наприклад, ви могли б придумати щось на зразок цього: Однак наведений вище код поверне SyntaxError:Це тому, що згідно з

  Відновіть сприйняття звуку за допомогою цих 11 програм для слухових апаратів

Специфікація PEP

:

>>> *string, = 'PythonIsTheBest'
>>> string
['P', 'y', 't', 'h', 'o', 'n', 'I', 's', 'T', 'h', 'e', 'B', 'e', 's', 't']

Кортеж (або список) ліворуч від простого призначення

>>> *numbers, = range(5)
>>> numbers
[0, 1, 2, 3, 4]

Якщо ми хочемо розпакувати всі значення iterable в одну змінну, ми повинні встановити кортеж, отже, додавання простої коми буде достатньо:

Іншим прикладом може бути використання функції діапазону, яка повертає послідовність чисел.

Тепер, коли ви знаєте, як розпаковувати списки та кортежі із зірочкою, настав час перейти до розпакування словників.

Як розпакувати словники за допомогою оператора **

>>> **greetings, = {'hello': 'HELLO', 'bye':'BYE'} 
...
SyntaxError: invalid syntax

У той час як одна зірочка використовується для розпакування списків і кортежів, подвійна зірочка (**) використовується для розпакування словників.

>>> food = {'fish':3, 'meat':5, 'pasta':9} 
>>> colors = {'red': 'intensity', 'yellow':'happiness'}
>>> merged_dict = {**food, **colors}
>>> merged_dict
{'fish': 3, 'meat': 5, 'pasta': 9, 'red': 'intensity', 'yellow': 'happiness'}

На жаль, ми не можемо розпакувати словник до однієї змінної, як ми робили з кортежами та списками. Це означає, що наступне викличе помилку:

Однак ми можемо використовувати оператор ** всередині callables та інших словників. Наприклад, якщо ми хочемо створити об’єднаний словник з інших словників, ми можемо використати наведений нижче код:

Це досить короткий спосіб створення складених словників, однак це не основний підхід до розпакування в Python.

Давайте подивимося, як ми можемо використовувати розпакування з callables

Упаковка у функції: арги та кварги

Ви, мабуть, бачили, як args і kwargs були реалізовані в класах або функціях. Давайте подивимося, чому нам потрібно використовувати їх разом із callables.

>>> def product(n1, n2):
...     return n1 * n2
... 
>>> numbers = [12, 1]
>>> product(*numbers)
12

Упаковка з оператором * (args)

>>> product(12, 1)
12

Припустимо, у нас є функція, яка обчислює добуток двох чисел.

>>> numbers = [12, 1, 3, 4]
>>> product(*numbers)
...
TypeError: product() takes 2 positional arguments but 4 were given

Як бачите, ми розпаковуємо номери списків у функцію, тож ми фактично виконуємо наступне:

>>> def product(*args):
...     result = 1
...     for i in args:
...             result *= i
...     return result
...
>>> product(*numbers)
144

До цього моменту все працює чудово, але що, якщо ми хочемо передати довший список? Це, звичайно, викличе помилку, оскільки функція отримує більше аргументів, ніж вона здатна впоратися.

Ми можемо вирішити все це, просто запакувавши список безпосередньо у функції, що створює ітерацію всередині нього та дозволяє нам передавати будь-яку кількість аргументів у функцію.

Тут ми розглядаємо параметр args як ітерацію, проходимо по його елементах і повертаємо добуток усіх чисел. Зверніть увагу, що початкове число результату має бути одиницею, тому що якщо ми починаємо з нуля, функція завжди повертатиме нуль. Примітка: args — це лише домовленість, ви можете використовувати будь-яку іншу назву параметраМи також можемо передати довільні числа до функції без використання списку, як і з вбудованим

>>> product(5, 5, 5)
125
>>> print(5, 5, 5)
5 5 5

функція друку

>>> def test_type(*args):
...     print(type(args))
...     print(args)
... 
>>> test_type(1, 2, 4, 'a string')
<class 'tuple'>
(1, 2, 4, 'a string')

.

Нарешті, давайте отримаємо тип об’єкта аргументів функції.

Як зазначено у наведеному вище коді, тип аргументів завжди буде кортежем, а його вміст складатиметься з усіх аргументів без ключів, що передаються функції.

  Експериментальний маркетинг, пояснення за 5 хвилин (з прикладами)

Упаковка з оператором ** (kwargs)

>>> def make_person(name, **kwargs):
...     result = name + ': '
...     for key, value in kwargs.items():
...             result += f'{key} = {value}, '
...     return result
... 
>>> make_person('Melissa', id=12112, location='london', net_worth=12000)
'Melissa: id = 12112, location = london, net_worth = 12000, '

Як ми бачили раніше, оператор ** використовується виключно для словників. Це означає, що за допомогою цього оператора ми можемо передати функції ключ-значення як параметр.

Давайте створимо функцію make_person, яка отримує позиційний аргумент «ім’я» та невизначену кількість аргументів із ключовими словами.

Як ви бачите, оператор **kwargs перетворює всі аргументи з ключовими словами у словник, який ми можемо повторювати у функції.

>>> def test_kwargs(**kwargs):
...     print(type(kwargs))
...     print(kwargs)
... 
>>> test_kwargs(random=12, parameters=21)
<class 'dict'>
{'random': 12, 'parameters': 21}

Примітка: kwargs — це лише домовленість, ви можете називати цей параметр як завгодно

Ми можемо перевірити тип kwargs так само, як ми робили з args:

>>> def my_final_function(*args, **kwargs):
...     print('Type args: ', type(args))
...     print('args: ', args)
...     print('Type kwargs: ', type(kwargs))
...     print('kwargs: ', kwargs)
... 
>>> my_final_function('Python', 'The', 'Best', language="Python", users="A lot")
Type args:  <class 'tuple'>
args:  ('Python', 'The', 'Best')
Type kwargs:  <class 'dict'>
kwargs:  {'language': 'Python', 'users': 'A lot'}

Внутрішня змінна kwargs завжди стає словником, який зберігає пари ключ-значення, передані функції.

Нарешті, давайте використаємо args і kwargs в одній функції:

Висновок

  • Оператори розпакування дійсно корисні в повсякденних завданнях, тепер ви знаєте, як використовувати їх як в окремих операторах, так і в параметрах функцій.
  • У цьому посібнику ви дізналися:
  • Ви використовуєте * для кортежів і списків і ** для словників
  • Ви можете використовувати оператори розпакування в конструкторах функцій і класів

args використовуються для передачі неключових параметрів у функціїkwargs використовуються для передачі ключових параметрів функціям.