Mester Python-utpakking: Lær å bruke * og ** effektivt!

Utforsk Kraften i Python: Utpakking av Data

Python er et av de mest populære programmeringsspråkene, og i denne artikkelen skal vi dykke ned i en sentral, men ofte oversett funksjonalitet: utpakking. Mange har sett symbolene * og ** i kode, kanskje til og med brukt dem, uten fullt ut å forstå deres rolle. Vi skal utforske konseptet utpakking og se hvordan det kan hjelpe deg å skrive mer elegant og effektiv Python-kode.

Følgende nøkkelbegreper vil være nyttige å ha i bakhodet:

  • Iterable: En sekvens som kan itereres gjennom med en for-løkke, som lister, tupler, sett og ordbøker.
  • Callable: Et Python-objekt som kan kalles ved hjelp av parenteser (), for eksempel en funksjon min_funksjon().
  • Shell: Et interaktivt kjøringsmiljø der vi kan skrive og teste Python-kode. Dette startes ved å skrive python i en terminal.
  • Variabel: Et symbolsk navn som refererer til et lagret objekt i minnet.

Før vi går videre, la oss adressere en vanlig misforståelse. Asterisk-symbolene i Python har også en rolle som aritmetiske operatorer. En enkelt stjerne * brukes til multiplikasjon, mens to stjerner ** brukes til eksponentiering. For eksempel:

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

Dette kan vi teste i et interaktivt Python-shell. (Merk at du må ha Python 3 installert for å følge denne artikkelen.) Som vi ser, brukes stjernene som operatorer når de står mellom to numeriske verdier.

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

Men når stjernene * og ** plasseres foran en iterable (som en liste, tuppel eller ordbok), har de en annen betydning: De brukes til utpakking. Vi skal nå se nærmere på dette.

Hva er egentlig utpakking?

Utpakking refererer til prosessen med å trekke ut elementer fra en iterable, som lister, tupler og ordbøker. Du kan se for deg at det er som å åpne en eske og plukke ut innholdet, enten det er kabler, hodetelefoner eller en USB-minnepinne.

La oss se et eksempel:

    >>> mybox = ['kabler', 'hodetelefoner', 'USB']
    >>> item1, item2, item3 = mybox
  

I dette tilfellet tildeler vi elementene fra mybox-listen til de tre variablene item1, item2 og item3. Dette er det grunnleggende prinsippet i utpakking i Python.

    >>> item1
    'kabler'
    >>> item2
    'hodetelefoner'
    >>> item3
    'USB'
  

Hvis vi sjekker verdiene av variablene, ser vi at item1 refererer til «kabler», item2 til «hodetelefoner», og så videre. Hva skjer hvis vi prøver å pakke ut en liste med et annet antall elementer enn variabler? La oss teste det:

    >>> newbox = ['kabler', 'hodetelefoner', 'USB', 'mus']
    >>> item1, item2, item3 = newbox
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: for mange verdier å pakke ut (forventet 3)
  

Som forventet får vi en feil. Python vet ikke hvilke verdier som skal tildeles de tre variablene når det er fire verdier å pakke ut. Dette fører til en ValueError med meldingen «for mange verdier å pakke ut». Det samme skjer dersom det er for få verdier:

    >>> lastbox = ['kabler', 'hodetelefoner']
    >>> item1, item2, item3 = lastbox
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: ikke nok verdier å pakke ut (forventet 3, fikk 2)
  

Det er viktig å huske at utpakking ikke er begrenset til lister, det kan brukes med alle iterables (lister, sett, tupler, ordbøker). Så, hvordan kan vi unngå disse feilene og pakke ut alle elementer fra en iterable til et par variabler uten feil?

Utpakkingsoperatoren: Stjerneoperatoren (*)

Svaret er å bruke utpakkingsoperatoren *. La oss se hvordan den fungerer:

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

Her tildeles det første og siste elementet i listen til first og last, mens alle de mellomliggende elementene tildeles unused som en ny liste. Hvis vi bare er interessert i å ignorere de mellomliggende verdiene kan vi bruke en understrek _ som en «dummy-variabel»:

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

Dette trikset fungerer også dersom listen kun har to elementer:

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

I dette tilfellet vil understrekvariabelen bare lagre en tom liste. Det er også viktig å vite at vi kun kan pakke ut alle verdier av en iterable til én variabel ved hjelp av en tuppel. For eksempel:

     >>> *string, = 'PythonErBest'
    >>> string
    ['P', 'y', 't', 'h', 'o', 'n', 'E', 'r', 'B', 'e', 's', 't']
  

En feil vil bli kastet dersom vi ikke inkluderer kommaet etter variabelnavnet:

   >>> *string = 'PythonErBest'
      File "<stdin>", line 1
    SyntaxError: stjernet tildelingsmål må være i en liste eller tuppel
  

Et annet eksempel er å bruke funksjonen range som returnerer en sekvens av tall:

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

Nå som vi har sett hvordan vi pakker ut lister og tupler, la oss se på hvordan vi kan pakke ut ordbøker.

Utpakking av Ordbøker med Dobbel Stjerne-Operatoren (**)

Mens en enkel stjerne * brukes for lister og tupler, brukes dobbel stjerne ** for å pakke ut ordbøker.

    >>> **greetings, = {'hei': 'HEI', 'hade':'HADE'} 
    ...
    SyntaxError: ugyldig syntaks
    

Vi kan ikke pakke ut en ordbok til en enkelt variabel på samme måte som vi gjorde med lister og tupler, slik som vist over. Derimot, kan vi bruke **-operatoren for å slå sammen ordbøker eller sende ordbøker som argumenter til funksjoner. For eksempel:

    >>> mat = {'fisk':3, 'kjøtt':5, 'pasta':9} 
    >>> farger = {'rød': 'intensitet', 'gul':'glede'}
    >>> merged_dict = {**mat, **farger}
    >>> merged_dict
    {'fisk': 3, 'kjøtt': 5, 'pasta': 9, 'rød': 'intensitet', 'gul': 'glede'}
  

I dette eksemplet slår vi sammen to ordbøker mat og farger til en ny ordbok merged_dict.

Utpakking i Funksjoner: *args og **kwargs

Du har kanskje sett *args og **kwargs i funksjonsdefinisjoner tidligere. La oss se på deres rolle.

Utpakking med *-operatoren (*args)

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

La oss si at vi har en funksjon produkt som multipliserer to tall. Vi kan pakke ut en liste med to tall og sende de som argumenter til funksjonen. Hva skjer dersom vi prøver å sende inn en liste med flere elementer?

    >>> numbers = [12, 1, 3, 4]
    >>> produkt(*numbers)
    ...
    TypeError: produkt() tar 2 posisjonsargumenter, men 4 ble gitt
  

Dette gir oss en feilmelding. Funksjonen forventer kun to argumenter. Det er her *args kommer inn. Ved å bruke *args som parameter i en funksjonsdefinisjon, kan vi akseptere et vilkårlig antall argumenter:

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

I dette eksemplet tar *args imot alle argumentene som en tuppel. Vi kan iterere gjennom tuppelen og returnere produktet av alle tallene. args er en konvensjon. Vi kunne brukt et hvilket som helst annet navn. Vi kan også sende en vilkårlig mengde tall til funksjonen uten å bruke en liste. Som vist med funksjonen print():

    >>> produkt(5, 5, 5)
    125
    >>> print(5, 5, 5)
    5 5 5
  

La oss til slutt se på typen til args

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

Som vi ser er typen til args alltid en tuppel.

Utpakking med **-operatoren (**kwargs)

      >>> def lag_person(navn, **kwargs):
    ...     resultat = navn + ': '
    ...     for key, value in kwargs.items():
    ...             resultat += f'{key} = {value}, '
    ...     return resultat
    ... 
    >>> lag_person('Melissa', id=12112, sted='london', netto_verdi=12000)
    'Melissa: id = 12112, sted = london, netto_verdi = 12000, '
  

Som vi så tidligere, brukes **-operatoren for ordbøker. Dette betyr at vi med denne operatoren kan sende nøkkel-verdi-par til funksjonen som parametere. I eksemplet over definerer vi en funksjon lag_person. **kwargs konverterer alle nøkkelordsargumentene til en ordbok, som vi kan iterere over i funksjonen.

La oss se typen til kwargs:

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

Som vi ser er typen til kwargs en ordbok.

Til slutt, la oss bruke *args og **kwargs i samme funksjon:

      >>> def min_siste_funksjon(*args, **kwargs):
    ...     print('Type args: ', type(args))
    ...     print('args: ', args)
    ...     print('Type kwargs: ', type(kwargs))
    ...     print('kwargs: ', kwargs)
    ... 
    >>> min_siste_funksjon('Python', 'Det', 'Beste', språk="Python", brukere="Mange")
    Type args:  <class 'tuple'>
    args:  ('Python', 'Det', 'Beste')
    Type kwargs:  <class 'dict'>
    kwargs:  {'språk': 'Python', 'brukere': 'Mange'}
  

Konklusjon

Utpakkingsoperatorer er svært nyttige i hverdagsarbeid. I denne artikkelen har vi lært:

  • Hvordan bruke * for tupler og lister, og ** for ordbøker.
  • At utpakkingsoperatorer kan brukes i funksjoner og klassekonstruktører.
  • *args brukes for å sende posisjonsargumenter til funksjoner.
  • **kwargs brukes for å sende nøkkelordsargumenter til funksjoner.

Med denne kunnskapen vil du være i stand til å skrive mer effektiv og lesbar Python-kode.