Hur använder man uppackningsoperatorerna (*, **) i Python?

Python är det mest använda programmeringsspråket. Idag kommer du att lära dig att använda en av dess kärnfunktioner – men ofta ignorerade – genom att packa upp i Python.

Du har förmodligen sett * och ** i andras kod eller till och med använt dem utan att egentligen veta vad deras syfte är. Vi kommer att gå igenom konceptet med uppackning och hur man använder det för att skriva mer Pythonic-kod.

Här är en lista över begrepp som du kommer att ha nytta av när du läser den här handledningen:

  • Iterable: Vilken sekvens som helst som kan itereras av en for-loop, som set, listor, tupler och ordböcker
  • Callable: Ett Python-objekt som kan anropas med dubbel parentes (), till exempel myfunction()
  • Shell: Interaktiv körtidsmiljö som låter oss köra Python-kod. Vi kan kalla det genom att köra ”python” i en terminal
  • Variabel: Symboliskt namn som lagrar ett objekt och har en reserverad minnesplats.

Låt oss börja med den vanligaste förvirringen: Asteristiker i Python är också aritmetiska operatorer. En asterisk

används för multiplikation, medan två av dem (**) hänvisar till exponentiering.

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

Vi kan bevisa det genom att öppna ett Python-skal och skriva:

Obs: Du måste ha Python 3 installerat för att följa med den här handledningen. Om du inte har det installerat, kolla in vår Python-installationsguide.

Som du kan se använder vi asterisken efter den första siffran och före den andra. När du ser detta betyder det att vi använder aritmetiska operatorer.

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

Å andra sidan använder vi asteriskerna (*, **) före en iterable för att packa upp den – till exempel:

Oroa dig inte om du inte förstår det, detta är bara en inledning till uppackning i Python. Så fortsätt och läs hela handledningen!

Vad är det som packar upp?

Uppackning är processen att få ut saker – iterables som listor, tupler och ordböcker. Se det som att öppna en låda och ta ut olika föremål som kablar, hörlurar eller en USB.

Att packa upp i Python liknar att packa upp en låda i verkligheten.

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

Låt oss översätta samma exempel till kod för en bättre förståelse:

Som du kan se tilldelar vi de tre objekten i mybox-listan till tre variabler item1, item2, item2. Den här typen av variabeltilldelning är det grundläggande konceptet för uppackning i Python.

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

Om du försöker ta reda på värdet av varje föremål, kommer du att märka att artikel 1, hänvisar till ”kablar”, objekt 2, hänvisar till ”hörlurar” och så vidare.

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

Fram till här verkar allt vara bra med den här koden, men tänk om vi ville packa upp en lista med fler element i den – behålla samma mängd tilldelade variabler?

Det är förmodligen du förväntade dig den här typen av fel. I huvudsak tilldelar vi 4 listobjekt till tre variabler, hur lyckas Python tilldela rätt värden? Det gör det inte, det är för att vi får en ValueError

med meddelandet ”för många värden att packa upp”. Det här händer eftersom vi ställer in tre variabler till vänster och fyra värden (motsvarande newbox-listan) till höger.

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

Om du försöker göra en liknande process, men med fler variabler än värden att packa upp, får du ett annat ValueError förutom det med ett något annat meddelande:

Obs: Vi har arbetat med listor men du kan använda den här formen av uppackning med vilken som helst iterabel (listor, uppsättningar, tupler, ordböcker)

Så hur övervinner vi denna situation? Finns det något sätt att packa upp alla objekt i en iterabel till ett par variabler utan att få några fel?

Visst finns det, och det kallas uppackningsoperatör eller asteriskoperator (*, **). Låt oss se hur man använder det i Python.

Hur man packar upp listor med *-operatören

Asteriskoperatorn

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

används för att packa upp alla värden för en iterabel som inte har tilldelats ännu.

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

Låt oss anta att du vill få det första och sista elementet i en lista utan att använda index, vi skulle kunna göra det med asteriskoperatorn:

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

Som du förstår får vi alla oanvända värden med asteriskoperatorn. Det föredragna sättet att kassera värden är att använda en understrecksvariabel (_), som ibland används som en ”dummy-variabel”.

Vi kan fortfarande använda det här tricket även om listan bara har två element:

I det här fallet lagrar understrecksvariabeln (dummyvariabel) en tom lista så att de andra två variablerna runt dem kan komma åt de tillgängliga värdena i listan.

>>> *string = 'PythonIsTheBest'

Vanlig felsökning

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

Vi kan packa upp ett unikt element i en iterabel. Till exempel skulle du komma på något sånt här: Ovanstående kod kommer dock att returnera ett SyntaxError:Detta eftersom enligt

PEP-specifikation

:

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

En tuppel (eller lista) på vänster sida av en enkel uppgift

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

Om vi ​​vill packa upp alla värden för en iterabel till en enda variabel måste vi ställa in en tupel, så det räcker med att lägga till ett enkelt kommatecken:

Ett annat exempel skulle vara att använda intervallfunktionen, som returnerar en sekvens av tal.

Nu när du vet hur man packar upp listor och tuplar med en asterisk, är det dags att börja packa upp ordböcker.

Hur man packar upp ordböcker med **-operatören

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

Medan en enstaka asterisk används för att packa upp listor och tuplar, används dubbelasterisken (**) för att packa upp ordböcker.

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

Tyvärr kan vi inte packa upp en ordbok till en enda variabel som vi har gjort med tupler och listor. Det betyder att följande kommer att ge ett fel:

Däremot kan vi använda **-operatören inuti callables och andra ordböcker. Om vi ​​till exempel vill skapa en sammanslagen ordbok, gjord av andra ordböcker, kan vi använda koden nedan:

Detta är ett ganska kort sätt att skapa sammansatta ordböcker, men detta är inte huvudmetoden för uppackning i Python.

Låt oss se hur vi kan använda uppackning med callables

Packning i funktioner: args och kwargs

Du har förmodligen sett args och kwargs tidigare antingen implementerade på klasser eller funktioner. Låt oss se varför vi behöver använda dem tillsammans med callables.

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

Packning med *-operatören (args)

>>> product(12, 1)
12

Anta att vi har en funktion som beräknar produkten av två tal.

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

Som du kan se packar vi upp listnumren till funktionen, så vi kör faktiskt följande:

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

Fram till här fungerar allt bra, men tänk om vi ville klara en längre lista? Det kommer säkert att skapa ett fel eftersom funktionen tar emot fler argument än den kan hantera.

Vi kan lösa allt detta bara genom att packa listan direkt på funktionen, vilket skapar en itererbar insida av den och låter oss skicka valfritt antal argument till funktionen.

Här behandlar vi parametern args som en iterabel, går över dess element och returnerar produkten av alla siffror. Notera hur startnumret på resultatet måste vara ett för om vi börjar med noll kommer funktionen alltid att returnera noll. Obs: args är bara en konvention, du kan använda vilket annat parameternamn som helstVi skulle också kunna skicka godtyckliga nummer till funktionen utan att använda en lista, precis som med den inbyggda

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

utskriftsfunktion

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

.

Låt oss slutligen få objekttypen för en funktions args.

Som noterats i ovanstående kod kommer typen av args alltid att vara tuppel, och innehållet i den kommer att vara alla argument utan nyckelord som skickas till funktionen.

Packning med **-operatören (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, '

Som vi såg tidigare används operatorn ** uteslutande för ordböcker. Det betyder att vi med den här operatorn kan skicka nyckel-värdepar till funktionen som en parameter.

Låt oss skapa en funktion make_person, som får ett positionsargument ”namn” och en odefinierad mängd nyckelordsargument.

Som du kan se konverterar **kwargs-satsen alla nyckelordsargument till en ordbok, som vi kan iterera inuti funktionen.

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

Obs: kwargs är bara en konvention du kan namnge denna parameter med vad du vill

Vi kan kontrollera typen av kwargs på samma sätt som vi gjorde med 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'}

Den interna variabeln kwargs blir alltid en ordbok, som lagrar nyckel-värdeparen som skickas till funktionen.

Slutligen, låt oss använda args och kwargs i samma funktion:

Slutsats

  • Uppackningsoperatorer är verkligen användbara i dagliga uppgifter, nu vet du hur du använder dem både i individuella satser och funktionsparametrar.
  • I den här handledningen lärde du dig:
  • Du använder * för tupler och listor och ** för ordböcker
  • Du kan använda uppackningsoperatorer i funktioner och klasskonstruktörer

args används för att skicka parametrar utan nyckelord till funktionerkwargs används för att skicka nyckelordsparametrar till funktioner.