Programmering

3 trinn til en Python async-overhaling

Python er en av mange språk som støtter en måte å skrive asynkrone programmer på - programmer som skifter fritt mellom flere oppgaver, alt på en gang, slik at ingen oppgaver holder fremdriften til de andre.

Sjansen er imidlertid at du hovedsakelig har skrevet synkrone Python-programmer - programmer som bare gjør en ting om gangen og venter på at hver oppgave skal fullføres før du starter en annen. Å flytte til asynkronisering kan være skurrende, da det krever ikke bare ny syntaks, men også nye måter å tenke på koden sin.

I denne artikkelen vil vi undersøke hvordan et eksisterende, synkront program kan gjøres om til et asynkront program. Dette innebærer mer enn bare å dekorere funksjoner med asynk syntaks; det krever også å tenke annerledes på hvordan programmet vårt kjører, og å avgjøre om async til og med er en god metafor for hva det gjør.

[Også på: Lær Python-tips og triks fra Serdar Yegulalps Smart Python-videoer]

Når skal du bruke asynkronisering i Python

Et Python-program egner seg best for asynkronisering når det har følgende egenskaper:

  • Det prøver å gjøre noe som for det meste er bundet av I / O eller ved å vente på at en ekstern prosess skal fullføres, som et nettverk som leser lenge.
  • Det prøver å gjøre en eller flere av slike oppgaver samtidig, samtidig som det muligens håndterer brukerinteraksjoner.
  • Oppgavene det er snakk om er ikke beregningstunge.

Et Python-program som bruker threading er vanligvis en god kandidat for å bruke async. Trådene i Python er samarbeidsvillige; de gir hverandre etter behov. Asynkroniseringsoppgaver i Python fungerer på samme måte. I tillegg tilbyr async visse fordeler i forhold til tråder:

  • De asynkronisering/avvente syntaks gjør det enkelt å identifisere de asynkrone delene av programmet ditt. Derimot er det ofte vanskelig å fortelle et øyeblikk hvilke deler av en app som kjøres i en tråd.
  • Fordi asynkroniseringsoppgaver deler samme tråd, administreres alle data de får tilgang til automatisk av GIL (Pythons opprinnelige mekanisme for synkronisering av tilgang til objekter). Tråder krever ofte komplekse mekanismer for synkronisering.
  • Asynkroniseringsoppgaver er lettere å administrere og avbryte enn tråder.

Å bruke async er ikke anbefales hvis Python-programmet ditt har disse egenskapene:

  • Oppgavene har høye beregningskostnader - for eksempel gjør de tungt antall knusing. Tungt beregningsarbeid håndteres best med multiprosessering, som lar deg vie en hel maskinvare tråd til hver oppgave.
  • Oppgavene tjener ikke på å bli sammenflettet. Hvis hver oppgave avhenger av den siste, er det ingen vits å få dem til å kjøre asynkront. Når det er sagt, hvis programmet innebærersettene av serieoppgaver, kan du kjøre hvert sett asynkront.

Trinn 1: Identifiser de synkrone og asynkrone delene av programmet ditt

Python async-kode må lanseres av og administreres av de synkrone delene av Python-applikasjonen. For det formål er din første oppgave når du konverterer et program til asynkronisering, å tegne en linje mellom synkroniserings- og asynkroniseringsdelene i koden din.

I vår forrige artikkel om asynkronisering brukte vi en nettskraper-app som et enkelt eksempel. De asynkroniserte delene av koden er rutinene som åpner nettverkstilkoblingene og leser fra nettstedet - alt du vil blande. Men den delen av programmet som sparker alt det av er ikke asynkronisert; den starter asynkroniseringsoppgavene og lukker dem grasiøst når de er ferdig.

Det er også viktig å skille ut potensieltblokkeringsoperasjon fra asynkronisering, og hold den i synkroniseringsdelen av appen din. Å lese brukerinngang fra konsollen blokkerer for eksempel alt inkludert async hendelsessløyfe. Derfor vil du håndtere brukerinngang enten før du starter asynkroniseringsoppgaver eller etter at du er ferdig med dem. (Den er mulig å håndtere brukerinngang asynkront via multiprosessering eller threading, men det er en avansert øvelse vi ikke kommer inn på her.)

Noen eksempler på blokkeringsoperasjoner:

  • Konsollinngang (som vi nettopp beskrev).
  • Oppgaver som involverer tung CPU-bruk.
  • Ved hjelp av tid. sove å tvinge en pause. Merk at du kan sove inne i en asynkroniseringsfunksjon ved å bruke asyncio. sove som erstatning for tid. sove.

Trinn 2: Konverter passende synkroniseringsfunksjoner til asynkroniseringsfunksjoner

Når du vet hvilke deler av programmet som skal kjøres asynkront, kan du dele dem opp i funksjoner (hvis du ikke allerede har gjort det) og gjøre dem om til asynkroniseringsfunksjoner med asynkronisering nøkkelord. Du må da legge til kode i den synkrone delen av applikasjonen for å kjøre asynkroniseringskoden og samle resultater fra den om nødvendig.

Merk: Du vil sjekke anropskjeden til hver funksjon du har gjort asynkron, og sørge for at de ikke påkaller en potensielt langvarig eller blokkerende operasjon. Async-funksjoner kan direkte kalle synkroniseringsfunksjoner, og hvis den synkroniseringsfunksjonen blokkeres, så gjør også async-funksjonen det.

La oss se på et forenklet eksempel på hvordan en konvertering av synkronisering til asynkronisering kan fungere. Her er vårt "før" -program:

def a_function (): # noen async-kompatibel handling som tar en stund def another_function (): # noen sync-funksjon, men ikke en blokkering av def def__stuff (): a_function () another_function () def main (): for _ in range (3): do_stuff () hoved () 

Hvis vi vil ha tre tilfeller av gjøre ting for å kjøre asynkroniserte oppgaver, må vi snu gjøre ting (og potensielt alt det berører) til asynkroniseringskode. Her er et første pass ved konverteringen:

importer asyncio async def a_function (): # noen async-kompatibel handling som tar litt tid def another_function (): # noen sync-funksjon, men ikke en blokkering async def do_stuff (): vent a_function () another_function () async def main ( ): oppgaver = [] for _ i rekkevidde (3): oppgaver.append (asyncio.create_task (do_stuff ())) venter på asyncio.gather (oppgaver) asyncio.run (main ()) 

Legg merke til endringene vi gjordehoved-. Nå hoved- bruker asyncio å starte hver forekomst av gjøre ting som en samtidig oppgave, og deretter venter på resultatene (asyncio.gather). Vi konverterte også a_funksjon inn i en asynkroniseringsfunksjon, siden vi ønsker alle forekomster av a_funksjon å løpe side om side, og sammen med andre funksjoner som trenger asynkronisering.

Hvis vi ønsket å gå et skritt videre, kunne vi også konvertere en annen_funksjon å synkronisere:

async def another_function (): # noen synkroniseringsfunksjon, men ikke en blokkering async def do_stuff (): avventer en_function () avventer another_function () 

Imidlertid å lageen annen_funksjon asynkron ville være overkill, siden (som vi har nevnt) det ikke gjør noe som vil blokkere fremdriften i programmet vårt. Også, hvis noen synkrone deler av programmet vårt kallesen annen_funksjon, må vi også konvertere dem til asynkronisering, noe som kan gjøre programmet vårt mer komplisert enn det trenger å være.

Trinn 3: Test Python async-programmet grundig

Ethvert asynk-konvertert program må testes før det går i produksjon for å sikre at det fungerer som forventet.

Hvis programmet ditt er beskjedent i størrelse - for eksempel et par dusin linjer eller så - og ikke trenger en full testpakke, bør det ikke være vanskelig å verifisere at det fungerer som forutsatt. Når det er sagt, hvis du konverterer programmet til asynkronisering som en del av et større prosjekt, hvor en testpakke er en standard armatur, er det fornuftig å skrive enhetstester for asynkroniserings- og synkroniseringskomponenter.

Begge de store testrammene i Python har nå en slags async-støtte. Pythons egneunittest rammeverk inkluderer test case-objekter for asynkroniseringsfunksjoner, og pytest tilbudpytest-asyncio for de samme målene.

Til slutt, når du skriver tester for asynkroniseringskomponenter, må du håndtere deres veldig asynkrone tilstand som en betingelse for testene. For eksempel er det ingen garanti for at asynkroniseringsjobber vil fullføres i den rekkefølgen de ble sendt inn. Den første kan komme inn sist, og noen vil kanskje aldri fullføre i det hele tatt. Eventuelle tester du designer for en async-funksjon, må ta hensyn til disse mulighetene.

Hvordan gjøre mer med Python

  • Kom i gang med asynkronisering i Python
  • Hvordan bruke asyncio i Python
  • Hvordan bruke PyInstaller for å lage Python-kjørbare filer
  • Cython tutorial: Hvordan øke hastigheten på Python
  • Slik installerer du Python på den smarte måten
  • Hvordan håndtere Python-prosjekter med poesi
  • Hvordan administrere Python-prosjekter med Pipenv
  • Virtualenv og venv: Python virtuelle miljøer forklart
  • Python virtualenv og venv do’s and don'ts
  • Python-tråder og underprosesser forklart
  • Hvordan bruke Python-feilsøkingsprogrammet
  • Hvordan bruke timeit til å profilere Python-kode
  • Hvordan bruke cProfile for å profilere Python-kode
  • Slik konverterer du Python til JavaScript (og tilbake igjen)
$config[zx-auto] not found$config[zx-overlay] not found