Sådan bruges kontrol af statisk type i Python 3.6

Fang automatisk mange almindelige fejl under kodning

En af de mest almindelige klager over Python-sproget er, at variabler er dynamisk typet. Det betyder, at du erklærer variabler uden at give dem en bestemt datatype. Typer tildeles automatisk til baseret på hvilke data, der blev sendt i:

I dette tilfælde oprettes variablen præsidentnavn som str-type, fordi vi passerede i en streng. Men Python vidste ikke, at det ville være en streng, indtil den faktisk kørte den kodelinje.

Til sammenligning skrives et sprog som Java statisk. For at oprette den samme variabel i Java, skal du erklære strengen eksplicit med en strengtype:

Fordi Java ved forudgående ved, at præsidentnavn kun kan indeholde en streng, vil det give dig en kompileringsfejl, hvis du prøver at gøre noget fjollet som at gemme et helt tal i det eller videregive det til en funktion, der forventer noget andet end en streng.

Hvorfor skulle jeg være interesseret i typer?

Det er normalt hurtigere at skrive ny kode på et dynamisk typisk sprog som Python, fordi du ikke behøver at udskrive alle typedeklarationer manuelt. Men når din kodebase begynder at blive stor, vil du uundgåeligt løbe ind i mange runtime-bugs, som statisk indtastning ville have forhindret.

Her er et eksempel på en utrolig almindelig slags bug i Python:

Alt, hvad vi gør, er at bede brugeren om deres navn og derefter udskrive "Hej, !". Og hvis brugeren ikke indtaster noget, vil vi udskrive "Hej, UserFirstName!" Som et tilbageslag.

Dette program vil fungere perfekt, hvis du kører det og indtaster et navn ... MEN det går ned, hvis du lader navnet være tomt:

Traceback (seneste opkald sidst):
  Fil "test.py", linje 14, i 
    first_name = get_first_name (fallback_name)
  Fil "test.py", linje 2, i get_first_name
    returner fuld_navn.split ("") [0]
AttributeError: 'dict' objekt har ingen attribut 'split'

Problemet er, at fallback_name ikke er en streng - det er en ordbog. Så kaldelse af get_first_name på fallback_name mislykkes forfærdeligt, fordi det ikke har en .split () -funktion.

Det er en simpel og indlysende fejl at rette, men hvad der gør denne fejl snigende er, at du aldrig vil vide, at fejlen eksisterer, før en bruger tilfældigvis kører programmet og efterlader navnet tomt. Du tester måske programmet tusind gange selv og bemærker aldrig denne enkle fejl, fordi du altid har skrevet et navn.

Statisk indtastning forhindrer denne type fejl. Inden du selv prøver at køre programmet, fortæller statisk indtastning dig, at du ikke kan videregive fallback_name til get_first_name (), fordi det forventer en str, men du giver det en dikt. Din kodeditor kan endda fremhæve fejlen, mens du skriver!

Når denne type bug sker i Python, er det normalt ikke i en simpel funktion som denne. Fejlen er normalt begravet flere lag nede i koden og udløst, fordi de indgivne data er lidt anderledes end tidligere forventet. For at debugge det, skal du genskabe brugerens input og finde ud af, hvor det gik galt. Så meget tid spildes fejlsøgning af disse let forebyggelige fejl.

Den gode nyhed er, at du nu kan bruge statisk indtastning i Python, hvis du vil. Og med Python 3.6 er der endelig en fornuftig syntaks til deklarering af typer.

Reparation af vores buggy-program

Lad os opdatere buggy-programmet ved at erklære typen for hver variabel og hver funktion input / output. Her er den opdaterede version:

I Python 3.6 erklærer du en variabel type som denne:

variabelnavn: type

Hvis du tildeler en startværdi, når du opretter variablen, er den så enkel som denne:

my_string: str = "Min strengværdi"

Og du erklærer en funktions input- og outputtyper som denne:

def funktionsnavn (parameter1: type) -> return_type:

Det er temmelig enkelt - bare en lille finjustering til den normale Python-syntaks. Men nu, hvor typerne er deklareret, se hvad der sker, når jeg kører typecheckeren:

$ mypy typing_test.py
test.py:16: fejl: Argument 1 til "get_first_name" har inkompatibel type Dict [str, str]; forventet "str"

Uden engang at udføre programmet, ved det, at der ikke er nogen måde, linje 16 fungerer på! Du kan rette fejlen lige nu uden at vente på, at en bruger finder ud af den tre måneder fra nu.

Og hvis du bruger en IDE som PyCharm, vil den automatisk tjekke typer og vise dig, hvor noget er galt, før du selv rammer "Kør":

Det er så let!

Flere Python 3.6-typeksempler på syntaks

Det er enkelt at erklære str- eller int-variabler. Hovedpinen opstår, når du arbejder med mere komplekse datatyper som indlejrede lister og ordbøger. Heldigvis er Python 3.6s nye syntaks til dette ikke så slemt - i det mindste ikke for et sprog, der tilføjede skrivning som en eftersmag.

Det grundlæggende mønster er at importere navnet på den komplekse datatype fra indtastningsmodulet og derefter videregive de indlejrede typer i parentes.

De mest almindelige komplekse datatyper, du bruger, er Dict, List og Tuple. Sådan ser det ud til at bruge dem:

fra at skrive import Dict, List
# En ordbog, hvor tasterne er strenge og værdierne er ints

name_counts: Dict [str, int] = {
    "Adam": 10,
    "Guido": 12
}
# En liste med heltal

tal: Liste [int] = [1, 2, 3, 4, 5, 6]
# En liste, der indeholder, viser, at hver har en strengnøgle / int-værdi

list_of_dicts: List [Dict [str, int]] = [
    {"key1": 1},
    {"key2": 2}
]

Tuples er lidt specielle, fordi de lader dig angive typen af ​​hvert element separat:

fra at skrive importtuple

my_data: Tuple [str, int, float] = ("Adam", 10, 5.7)

Du opretter også aliaser for komplekse typer bare ved at tildele dem til et nyt navn:

fra at skrive importliste, Tuple

LatLngVector = Liste [Tuple [float, float]]

point: LatLngVector = [
    (25.91375, -60.15503),
    (-11.01983, -166.48477),
    (-11.01983, -166.48477)
]

Nogle gange er dine Python-funktioner muligvis fleksible til at håndtere flere forskellige typer eller arbejde på en hvilken som helst datatype. Du kan bruge EU-typen til at erklære en funktion, der kan acceptere flere typer, og du kan bruge Enhver til at acceptere noget.

Python 3.6 understøtter også nogle af de smarte, typiske ting, du måske har set på andre programmeringssprog som generiske typer og brugerdefinerede brugerdefinerede typer.

Kørelse af typen Checker

Mens Python 3.6 giver dig denne syntaks til angivelse af typer, er der absolut intet i Python selv, der endnu gør noget med disse typedeklarationer. For faktisk at håndhæve typekontrol, skal du gøre en af ​​to ting:

  1. Download open source-mypy-typen, og kør den som en del af dine enhedstest eller udviklingsarbejdsgang.
  2. Brug PyCharm, der har indbygget typekontrol i IDE. Eller hvis du bruger en anden editor som Atom, skal du downloade dets egen type-kontrol af plug-in.

Jeg vil anbefale at gøre begge dele. PyCharm og mypy bruger forskellige typer kontrolimplementeringer, og de kan hver især fange ting, som den anden ikke gør. Du kan bruge PyCharm til kontrol i realtidstype og derefter køre mypy som en del af dine enhedstest som en endelig verifikation.

Store! Skal jeg begynde at skrive al min Python-kode med typedeklarationer?

Denne syntaks for typedeklaration er meget ny for Python - den fungerer kun fuldt ud fra Python 3.6. Hvis du viser din indtastede Python-kode til en anden Python-udvikler, er der en god chance for, at de tror, ​​at du er skør og ikke engang tror, ​​at syntaksen er gyldig. Og mypy-typen er stadig under udvikling og hævder ikke at være stabil endnu.

Så det kan være lidt tidligt at gå helt på dette til alle dine projekter. Men hvis du arbejder på et nyt projekt, hvor du kan gøre Python 3.6 til et minimumskrav, kan det være værd at eksperimentere med at skrive. Det har potentialet til at forhindre mange fejl.

En af de pæne ting er, at du nemt kan blande kode med og uden erklæringer af typen. Det er ikke alt-eller-intet. Så du kan starte med at erklære typer på de steder, der er mest værdifulde uden at ændre al din kode. Og da Python selv ikke vil gøre noget med disse typer under kørsel, kan du ikke ved et uheld bryde dit program ved at tilføje typedeklarationer.

Tak for at have læst! Hvis du er interesseret i maskinlæring (eller bare vil forstå, hvad det er), så tjek min maskinlæring er sjov! serie.

Du kan også følge mig på Twitter på @ageitgey eller finde mig på linkedin.