Jak używać operatorów rozpakowywania (*, **) w Pythonie?

Python jest najczęściej używanym językiem programowania. Dzisiaj nauczysz się korzystać z jednej z jego podstawowych — ale często ignorowanych — funkcji — rozpakowywania w Pythonie.

Prawdopodobnie widziałeś * i ** w kodzie innych, a nawet używałeś ich, nie wiedząc, jaki jest ich cel. Będziemy omawiać koncepcję rozpakowywania i jak jej używać do pisania większej ilości kodu w Pythonie.

Oto lista pojęć, które okażą się przydatne podczas czytania tego samouczka:

  • Iterowalna: Dowolna sekwencja, którą można iterować za pomocą pętli for, na przykład zestawy, listy, krotki i słowniki
  • Wywoływalny: Obiekt Pythona, który można wywołać za pomocą podwójnych nawiasów (), na przykład myfunction()
  • Shell: Interaktywne środowisko uruchomieniowe, które pozwala nam uruchamiać kod Pythona. Możemy to wywołać, uruchamiając „python” w terminalu
  • Zmienna: Nazwa symboliczna, która przechowuje obiekt i ma zarezerwowaną lokalizację w pamięci.

Zacznijmy od najczęstszego nieporozumienia: asterystyka w Pythonie to także operatory arytmetyczne. Jedna gwiazdka

służy do mnożenia, a dwa z nich (**) odnoszą się do potęgowania.

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

Możemy to udowodnić, otwierając powłokę Pythona i wpisując:

Uwaga: aby móc korzystać z tego samouczka, musisz mieć zainstalowany Python 3. Jeśli nie masz go zainstalowanego, sprawdź nasz przewodnik instalacji Pythona.

Jak widać, używamy gwiazdki po pierwszej liczbie i przed drugą. Kiedy to widzisz, oznacza to, że używamy operatorów arytmetycznych.

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

Z drugiej strony używamy gwiazdek (*, **) przed iteracją, aby ją rozpakować — na przykład:

Nie martw się, jeśli tego nie zrozumiesz, to tylko wstęp do rozpakowywania w Pythonie. Więc śmiało i przeczytaj cały samouczek!

Co się rozpakowuje?

Rozpakowywanie to proces wydobywania rzeczy — elementów iterowalnych, takich jak listy, krotki i słowniki. Pomyśl o tym jak o otwarciu pudełka i wyjęciu różnych przedmiotów, takich jak kable, słuchawki lub USB.

Rozpakowywanie w Pythonie jest podobne do rozpakowywania pudełka w prawdziwym życiu.

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

Przetłumaczmy ten sam przykład na kod dla lepszego zrozumienia:

Jak widać, przypisujemy trzy elementy z listy mybox do trzech zmiennych item1, item2, item2. Ten rodzaj przypisania zmiennych jest podstawową koncepcją rozpakowywania w Pythonie.

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

Jeśli spróbujesz uzyskać wartość każdej pozycji, zauważysz, że pozycja 1 odnosi się do „kable”, pozycja 2 odnosi się do „słuchawek” i tak dalej.

>>> 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)

Do tego momentu wszystko wydawało się być w porządku z tym kodem, ale co by było, gdybyśmy chcieli rozpakować listę z większą liczbą elementów — zachowując tę ​​samą liczbę przypisanych zmiennych?

Prawdopodobnie spodziewałeś się tego rodzaju błędu. Zasadniczo przypisujemy 4 elementy listy do trzech zmiennych, w jaki sposób Pythonowi udaje się przypisać właściwe wartości? Nie ma, to dlatego, że dostajemy Błąd wartości

z komunikatem „zbyt wiele wartości do rozpakowania”. Dzieje się tak, ponieważ ustawiamy trzy zmienne po lewej stronie i cztery wartości (odpowiadające liście newbox) po prawej stronie.

>>> 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)

Jeśli spróbujesz wykonać podobny proces, ale z większą liczbą zmiennych niż wartości do rozpakowania, otrzymasz kolejny błąd wartości, z tym wyjątkiem, że z nieco innym komunikatem:

Uwaga: Pracowaliśmy z listami, ale możesz użyć tej formy rozpakowywania z dowolnymi iterowalnymi (listami, zestawami, krotkami, słownikami)

Jak zatem przezwyciężyć tę sytuację? Czy jest jakiś sposób na rozpakowanie wszystkich elementów iterowalnego do kilku zmiennych bez żadnych błędów?

Jasne, że istnieje i nazywa się to operatorem rozpakowywania lub operatorem gwiazdki (*, **). Zobaczmy, jak go używać w Pythonie.

Jak rozpakowywać listy za pomocą operatora *

Operator gwiazdki

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

służy do rozpakowania wszystkich wartości elementu iterowalnego, które nie zostały jeszcze przypisane.

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

Załóżmy, że chcesz uzyskać pierwszy i ostatni element listy bez użycia indeksów, możemy to zrobić za pomocą operatora gwiazdki:

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

Jak możesz docenić, otrzymujemy wszystkie nieużywane wartości za pomocą operatora gwiazdki. Preferowanym sposobem odrzucania wartości jest użycie zmiennej podkreślenia (_), która jest czasami używana jako „zmienna fikcyjna”.

Nadal możemy korzystać z tej sztuczki, nawet jeśli lista zawiera tylko dwa elementy:

W tym przypadku zmienna podkreślenia (zmienna fikcyjna) przechowuje pustą listę, więc pozostałe dwie zmienne wokół niej mają dostęp do dostępnych wartości listy.

>>> *string = 'PythonIsTheBest'

Wspólne rozwiązywanie problemów

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

Możemy rozpakować unikalny element iterowalnego. Na przykład wymyśliłbyś coś takiego: Jednak powyższy kod zwróci błąd SyntaxError:To dlatego, że wg

Specyfikacja PEP

:

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

Krotka (lub lista) po lewej stronie prostego przypisania

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

Jeśli chcemy rozpakować wszystkie wartości zmiennej iterowalnej do jednej zmiennej, musimy ustawić krotkę, stąd wystarczy dodanie prostego przecinka:

Innym przykładem może być użycie funkcji zakresu, która zwraca sekwencję liczb.

Teraz, gdy wiesz, jak rozpakowywać listy i krotki z gwiazdką, nadszedł czas, aby przejść do rozpakowywania słowników.

Jak rozpakować słowniki za pomocą operatora **

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

Podczas gdy pojedyncza gwiazdka jest używana do rozpakowywania list i krotek, podwójna gwiazdka (**) jest używana do rozpakowywania słowników.

>>> 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'}

Niestety, nie możemy rozpakować słownika do pojedynczej zmiennej, tak jak robiliśmy to z krotkami i listami. Oznacza to, że następujące polecenie zgłosi błąd:

Możemy jednak użyć operatora ** wewnątrz callables i innych słowników. Na przykład, jeśli chcemy utworzyć połączony słownik utworzony z innych słowników, możemy użyć poniższego kodu:

Jest to dość krótki sposób tworzenia słowników złożonych, jednak nie jest to główne podejście do rozpakowywania w Pythonie.

Zobaczmy, jak możemy wykorzystać rozpakowywanie z wywołaniami

Pakowanie w funkcje: argumenty i kwargi

Prawdopodobnie widziałeś argumenty i kwargi przed implementacją w klasach lub funkcjach. Zobaczmy, dlaczego musimy ich używać wraz z wywołaniami.

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

Pakowanie z operatorem * (args)

>>> product(12, 1)
12

Załóżmy, że mamy funkcję, która oblicza iloczyn dwóch liczb.

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

Jak widać, rozpakowujemy numery list do funkcji, więc faktycznie wykonujemy następujące czynności:

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

Do tego momentu wszystko działa dobrze, ale co by było, gdybyśmy chcieli przekazać dłuższą listę? Z pewnością zgłosi błąd, ponieważ funkcja otrzymuje więcej argumentów, niż jest w stanie obsłużyć.

Możemy rozwiązać ten problem, po prostu umieszczając listę bezpośrednio w funkcji, co tworzy w niej element iterowalny i pozwala nam przekazać do funkcji dowolną liczbę argumentów.

Tutaj traktujemy parametr args jako iterowalny, przechodząc przez jego elementy i zwracając iloczyn wszystkich liczb. Zwróć uwagę, że początkowa liczba wyniku musi wynosić jeden, ponieważ jeśli zaczniemy od zera, funkcja zawsze zwróci zero. Uwaga: args to tylko konwencja, możesz użyć dowolnej innej nazwy parametruMożemy również przekazać do funkcji dowolne liczby bez użycia listy, tak jak w przypadku funkcji wbudowanej

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

funkcja drukowania

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

.

Na koniec pobierzmy typ obiektu argumentów funkcji.

Jak zauważono w powyższym kodzie, typem args będzie zawsze krotka, a jej treścią będą wszystkie argumenty nie będące słowami kluczowymi przekazane do funkcji.

Pakowanie z operatorem ** (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, '

Jak widzieliśmy wcześniej, operator ** jest używany wyłącznie w przypadku słowników. Oznacza to, że za pomocą tego operatora możemy przekazywać pary klucz-wartość do funkcji jako parametr.

Stwórzmy funkcję make_person, która otrzyma argument pozycyjny „nazwa” i nieokreśloną liczbę argumentów ze słowami kluczowymi.

Jak widać, instrukcja **kwargs konwertuje wszystkie argumenty ze słowami kluczowymi na słownik, który możemy iterować wewnątrz funkcji.

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

Uwaga: kwargs to tylko konwencja, w której możesz nazwać ten parametr, jak chcesz

Możemy sprawdzić typ kwarg w taki sam sposób, jak zrobiliśmy to z argumentami:

>>> 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'}

Zmienna wewnętrzna kwargs zawsze staje się słownikiem, w którym przechowywane są pary klucz-wartość przekazane do funkcji.

Na koniec użyjmy argumentów i kwarg w tej samej funkcji:

Wniosek

  • Operatory rozpakowywania są bardzo przydatne w codziennych zadaniach, teraz wiesz, jak ich używać zarówno w poszczególnych instrukcjach, jak i parametrach funkcji.
  • W tym samouczku nauczyłeś się:
  • Używasz * dla krotek i list, a ** dla słowników
  • Operatorów rozpakowujących można używać w konstruktorach funkcji i klas

args służą do przekazywania do funkcji parametrów innych niż słowa kluczowekwargs służą do przekazywania parametrów ze słowami kluczowymi do funkcji.