Hvordan man ikke længere er bange for Python

Et dykke ned i sprogreference dokumentationen

Kilde

I det første år eller to, da jeg begyndte at kode, troede jeg, at det at lære et sprog handlede om at lære syntaks. Så det er alt, hvad jeg gjorde.

Naturligvis blev jeg ikke til en stor udvikler. Jeg sad fast. Derefter, en fin dag, klikkede det bare. Jeg indså, at jeg gjorde det forkert. At lære syntaks skal være det mindste af mine bekymringer. Det, der betyder noget, er alt andet ved sproget. Hvad er det egentlig? Læs videre.

Denne artikel er opdelt i tre hoveddele: Datamodellen, udførelsesmodellen og den Lexical analyse.

Denne artikel er mere en indsigt i, hvordan ting fungerer i Pythonland - i modsætning til hvordan man lærer Python. Du finder mange, hvordan man lærer kilder online.

Hvad jeg ikke fandt online, var en enkelt kilde til almindelige 'gotchas' i Python. En kilde, der forklarer, hvordan sproget fungerer. Dette forsøger at løse dette problem. Jeg tror, ​​jeg er kommet op, der er så meget ved det!

Alt her kommer fra den officielle dokumentation. Jeg har kondenseret det - til de vigtige punkter, sorteret om ting og tilføjet mine eksempler. Alle links peger på dokumentationen.

Uden videre, her går vi.

Datamodel

Objekter, værdier og typer

Objekter er Pythons abstraktion for data.

Hvert objekt har sin unikke faste identitet, en fast type og en værdi.

'Fast' betyder, at et objekts identitet og type aldrig kan ændres.

Værdien kan ændre sig. Objekter, hvis værdi kan ændres, kaldes mutable, mens objekter, hvis værdi ikke kan ændres, kaldes uforanderlig.

Mutabiliteten bestemmes efter type:

  • Tal, strenge og tuples er uforanderlige
  • Lister og ordbøger er mutable

Objekternes identitet kan sammenlignes via is-operatoren.

id () returnerer identiteten

type () returnerer typen

Bemærk: Værdien af ​​et uforanderligt containerobjekt, der indeholder en henvisning til et mutérbart objekt, kan ændres, når sidstnævnte værdi ændres. Beholderen betragtes dog stadig som uforanderlig, fordi samlingen af ​​objekter, den indeholder, ikke kan ændres. Således er uforanderlighed ikke strengt det samme som at have en uforanderlig værdi.

Denne note fik hovedet til at dreje de første to gange, jeg læste det.

Enkel oversættelse: Uforanderlighed er ikke det samme som uforanderlig værdi. I nedenstående eksempel er tuplen uforanderlig, mens værdien konstant ændres (når listen ændres).

Eksempel:

>>> t = ("a", [1]) # en tuple af streng og liste
>>> id (t)
4372661064
>>> t
('a', [1])
>>> type (t)

>>> t [1]
[1]
>>> t [1] .append (2)
>>> t
('a', [1, 2])
>>> id (t)
4372661064
>>> type (t)

Tuplen er uforanderlig, selvom den indeholder et mutérbart objekt, en liste.

Sammenlign dette med en streng, hvor ændring af den eksisterende matrix ændrer objektet (da strenge er uforanderlige).

>>> x = "abc"
>>> id (x)
4371437472
>>> x + = "d"
>>> x
'Abcd'
>>> id (x)
4373053712

Her er navnet, x bundet til et andet objekt af typen streng. Dette ændrer også id.

Det oprindelige objekt, der er uforanderligt, forbliver uforanderligt. Bindingen forklares nærmere i det følgende, hvilket skulle gøre tingene klarere.

Indbyggede typer

Python leveres med flere indbyggede typer:

Ingen

Typen er repræsenteret af et enkelt objekt og dermed en enkelt værdi. Det eneste objekt med typen = NoneType

>>> type (Ingen)

numre

Dette er en samling abstrakte baseklasser, der bruges til at repræsentere tal. De kan ikke blive instantieret og int, flyde arve fra numre. Antal.

De er skabt af numeriske bogstaver og aritmetiske operationer. De returnerede genstande er uforanderlige, som vi har set. Følgende liste med eksempler gør dette klart:

>>> a = 3 + 4
>>> type (a)

>>> er instance (a, tal. antal)
Rigtigt
>>> er instance (a, tal. integreret)
Rigtigt
>>> er instance (3,14 + 2j, tal. Virkelig)
Falsk
>>> er instance (3.14 + 2j, tal.Kompleks)
Rigtigt

sekvenser

Disse repræsenterer endelige ordrede sæt indekseret med ikke-negative heltal. Ligesom en matrix fra andre sprog.

len () returnerer længden af ​​sekvenser. Når længden er n, har indekssættet elementer fra 0 ... n-1. Derefter vælges ith-elementet af seq [i-1].

I en sekvens l kan du vælge elementer mellem indekserne ved hjælp af snit: l [i: j].

Der er to typer sekvenser: mutérbar og uforanderlig.

  • Uændelige sekvenser inkluderer: strenge, tuples og bytes.
  • Blandbare sekvenser inkluderer: lister og byte-arrays

Indstiller

Disse repræsenterer uordnede, begrænsede sæt unikke, uforanderlige objekter. De kan ikke indekseres, men kan iteres. len () returnerer stadig antallet af varer i sættet.

Der er to typer sæt: mutable og immutable.

  • Et mutérbart sæt oprettes af sæt ().
  • Et uforanderligt sæt oprettes af frozenset ().

tilknytninger

Ordbog

Disse repræsenterer endelige sæt objekter indekseret med næsten vilkårlige værdier. Taster kan ikke være mutable objekter. Det inkluderer lister, andre ordbøger og andre objekter, der sammenlignes efter værdi og ikke med objektidentitet.

Dette betyder, at en frozenset også kan være en ordbogsnøgle!

moduler

Et modulobjekt er en grundlæggende organisatorisk enhed i Python. Navneområdet implementeres som en ordbog. Attributreferencer er opslag i denne ordbog.

For et modul m er ordbogen skrivebeskyttet, adgang til m .__ dict__.

Det er en almindelig ordbog, så du kan tilføje nøgler til den!

Her er et eksempel med Pythons Zen:

Vi tilføjer vores brugerdefinerede funktion, figur () til dette modul.

>>> importere dette som t
>>> t .__ dict__
{'__name__': 'dette', '__doc__': Ingen, '__package__': '',
.....
.....
's': "Gur Mra bs Clguba, ol Gvz Crgref \ n \ nOrnhgvshy vf orggre guna
vqrn. \ nAnzrfcnprf ner bar ubaxvat terng vqrn - yrg'f qb zber bs gubfr! ",
'd': {'A': 'N', 'B': 'O', 'C': 'P', 'D': 'Q', 'E': 'R', 'F': ' S',
'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k', 'y': 'l', 'z': 'm'},
'c': 97,
'i': 25
}
>>> def-tal ():
... print ("Kan du finde ud af Pythons Zen?")
...
>>> t.fig = figur
>>> t.fig ()
Kan du finde ud af Pythons Zen?
>>> t .__ dict__
{'__name__': 'dette', '__doc__': Ingen, '__package__': '',
.....
.....
's': "Gur Mra bs Clguba, ol Gvz Crgref \ n \ nOrnhgvshy vf orggre guna
vqrn. \ nAnzrfcnprf ner bar ubaxvat terng vqrn - yrg'f qb zber bs gubfr! ",
'd': {'A': 'N', 'B': 'O', 'C': 'P', 'D': 'Q', 'E': 'R', 'F': ' S',
'u': 'h', 'v': 'i', 'w': 'j', 'x': 'k', 'y': 'l', 'z': 'm'},
'c': 97,
'i': 25
'fig': 
}
>>> print ("". sammenføjning ([typget (c, c) for c in t.s]))
Zen af ​​Python, af Tim Peters
Smukke er bedre end grimme.
Eksplicit er bedre end implicit.
Enkelt er bedre end komplekst.
Kompleks er bedre end kompliceret.
Flad er bedre end indlejret.
Sparsom er bedre end tæt.
Læsbarhed tæller.
Specielle tilfælde er ikke specielle nok til at bryde reglerne.
Selvom praktiske forhold slår renhed.
Fejl bør aldrig passere lydløst.
Medmindre eksplicit tystes.
I lyset af tvetydighed skal du nægte fristelsen til at gætte.
Der skal være en-- og helst kun en - åbenlyst måde at gøre det på.
Selvom den måde måske ikke er indlysende i starten, medmindre du er hollandsk.
Nu er det bedre end aldrig.
Selvom det aldrig ofte er bedre end * lige nu.
Hvis implementeringen er vanskelig at forklare, er det en dårlig idé.
Hvis implementeringen er let at forklare, kan det være en god ide.
Navneområder er en god ide - lad os gøre mere af dem!

Ikke særlig nyttigt, men godt at vide.

Overbelastning af operatøren

Python muliggør overbelastning af operatøren.

Klasser har særlige funktionsnavne - metoder, de kan implementere til at bruge Pythons definerede operatører. Dette inkluderer skiver, aritmetiske operationer og abonnement.

For eksempel henviser __getitem __ () til abonnement. Derfor er x [i] ækvivalent med type (x) .__ getitem __ (x, i).

For at bruge operatøren [] på en klasse someClass: skal du definere __getitem __ () i someClass.

>>> klasse operatorTest (objekt):
... vals = [1,2,3,4]
... def __getitem __ (self, i):
... return self.vals [i]
...
>>> x = operatorTest ()
>>> x [2]
3
>>> x .__ getitem __ (2)
3
>>> type (x)

>>> type (x) .__ getitem __ (x, 2)
3
>>> OperatorTest .__ getitem __ (x, 2)
3

Forvirret over, hvorfor alle dem er ækvivalente? Det er til næste del - hvor vi dækker definitioner af klasse og funktion.

Ligeledes bestemmer funktionen __str __ () output, når str () -metoden kaldes på et objekt i din klasse.

Til sammenligningsoperationer er de specielle funktionsnavne:

  • objekt .__ lt __ (selv, andet) for <(“mindre end”)
  • objekt .__ le __ (selv, andet) for <= (“mindre end eller lig med”)
  • objekt .__ eq __ (selv, andet) for == (“lig med”)
  • objekt .__ ne __ (selv, andet) for! = (“ikke lig med”)
  • objekt .__ gt __ (selv, andet) for> ("større end")
  • objekt .__ ge __ (selv, andet) for> = ("større end eller lig med")

Så for eksempel kaldes x

Der er også specielle funktioner til aritmetiske operationer, som objekt .__ tilføj __ (selv, andre).

Som et eksempel kaldes x + y som x .__ tilføj __ (y)

En anden interessant funktion er __iter __ ().

Du kalder denne metode, når du har brug for en iterator til en container. Det returnerer et nyt iteratorobjekt, der kan iterere over alle objekter i containeren.

Ved kortlægning skal det iteres med tasterne på beholderen.

Selve iteratorobjektet understøtter to metoder:

  • iterator .__ iter __ (): Returnerer selve objektet.

Dette gør iteratorer og containerne ækvivalente.

Dette gør det muligt for iterator og containere at blive brugt til og i udsagn.

  • iterator .__ næste __ (): Returnerer det næste emne fra beholderen. Hvis der ikke er flere poster, hæver undtagelsen StopIteration.
klasse IterableObject (objekt): # Iterator-objektklassen
     vals = []
     det = 0
     def __init __ (self, val):
         self.vals = val
         det = 0
 
     def __iter __ (selv):
         vende tilbage selv
 
     def __næste __ (selv):
         hvis self.it 
>>> iter_object_example = IterableObject ([1,2,3])
>>> til val i iter_object_example:
... udskriv (val)
...
1
2
3
>>> iter_container_example = IterableClass ()
>>> til val i iter_container_example:
... udskriv (val)
...
1
2
3
4

Seje ting, ikke? Der er også en direkte ækvivalent i Javascript.

Context Managers implementeres også via overbelastning af operatører.

med åben (filnavn, 'r') som f

open (filnavn, 'r') er et context manager-objekt, der implementeres

objekt .__ indtast __ (selv) og

objekt .__ exit __ (self, exc_type, exc_value, traceback)
Alle ovenstående tre parametre er nul, når fejlen er Ingen.

klasse MyContextManager (objekt):
    def __init __ (self, some_stuff):
        self.object_to_manage = some_stuff
    def __enter __ (selv):
        print ("Indtastning af kontekststyring")
        return self.object_to_manage # kan også gøre nogle transformationer
    
    def __ exit __ (self, exc_type, exc_value, traceback):
        hvis exc_type er Ingen:
            print ("Forladt med succes")
            # Andre ting, der skal lukkes
>>> med MyContextManager ("fil") som f:
... udskriv (f)
...
Indtastning af kontekststyring
fil
Forladt

Dette er ikke nyttigt - men får poenget. Gør det alligevel nyttigt?

Philosoraptor

Udførelsesmodel

En blok er et stykke kode, der udføres som en enhed i en eksekveringsramme.

Eksempler på blokke inkluderer:

  • Moduler, som er blokke på øverste niveau
  • Funktionslegeme
  • Klasse definition
  • Men IKKE til sløjfer og andre kontrolstrukturer

Kan du huske, hvordan alt er et objekt i Python?

Nå, du har navne bundet til disse objekter. Disse navne er det, du synes om som variabler.

>>> x
Traceback (seneste opkald sidst):
  Fil "", linje 1, i 
NameError: navn 'x' er ikke defineret

Navnebinding eller tildeling sker i en blok.

Eksempler på navnebinding - disse er intuitive:

  • Parametre til funktioner er bundet til de navne, der er defineret i funktionen
  • Importangivelser binder modulets navn
  • Definitioner af klasse og funktion binder navnet til klasse / funktionsobjekter
  • Kontekthåndtere: med ... som f: f er det navn, der binder til ... objektet

Navne, der er bundet til en blok, er lokale for den blok. Det betyder, at globale variabler simpelthen er navne bundet til modulet.

Variabler, der bruges i en blok uden at være defineret, der er frie variabler.

Scopes definerer synligheden af ​​et navn i en blok. Omfanget af en variabel inkluderer den blok, den er defineret i, samt alle blokke indeholdt i den definerende blok.

Kan du huske, hvordan sløjfer ikke blokke? Derfor er iterationsvariabler, der er defineret i loopen, tilgængelige efter loopen, i modsætning til i C ++ og JavaScript.

>>> for i inden for rækkevidde (5):
... x = 2 * i
... udskriv (x, i)
...
0 0
2 1
4 2
6 3
8 4
>>> print (x, i) # uden for løkken! x blev defineret inde.
8 4

Når et navn bruges i en blok, løses det ved hjælp af det nærmeste lukkende omfang.

Bemærk: Hvis en navnebindende operation forekommer overalt i en kodeblokk, behandles al brug af navnet i blokken som henvisninger til den aktuelle blok. Dette kan føre til fejl, når et navn bruges i en blok, før det er bundet.

For eksempel:

>>> navn = "ydre_scope"
>>> def foo ():
... navn = "indre_funktion" hvis navn == "ydre_scope" \
                ellers "not_inner_function"
...
>>> foo ()
Traceback (seneste opkald sidst):
  Fil "", linje 1, i 
  Fil "", linje 2, i foo
UnboundLocalError: lokal variabel 'navn', der henvises til før tildeling

Dette er en vidunderlig traceback, som burde være fornuftig nu.

Vi har det øverste niveau, modulet - hvor der er en anden blok, funktionen. Hver binding inde i funktionen har funktionen som sit øverste niveau!

Når du binder navnenavnet til objektet "indre_funktion": før bindingen kontrollerer du dets værdi. Reglen siger, at du ikke kan henvise til den før bindingen. Præcis årsagen til UnboundLocalError.

Ikke denne form for udførelsesmodel? Kilde

Leksikalsk analyse

Python giver dig mulighed for at bruge linjeforbindelser. Brug en backslash til eksplicit at fortsætte linjer.

Kommentarer er ikke tilladt efter linjedeltagelser.

hvis en <10 og b <10 \ # Kommentar resulterer i SyntaxError
og c <10: # Kommentar okay
    vende tilbage sandt
andet:
    return False

Implicit forekommer linjeforbindelse på egen hånd, når elementer er inde i seler. Kommentarer her er tilladt.

month_names = ['Januari', 'Februari', 'Maart', # Disse er
               'April', 'Mei', 'Juni', # hollandske navne
               'Juli', 'Augustus', 'September', # i månederne
               'Oktober', 'November', 'december'] # året

Indrykning

Antallet af mellemrum / faner i indrykket betyder ikke noget, så længe det stiger for ting, der skal indrykkes. Den første linje bør ikke være indrykket.

De fire mellemrumsregler er en konvention defineret af PEP 8: Style Guide. Det er god praksis at følge den.

# Beregn listen over alle permutationer af l.
def perm (l):
        # Kommentarindrykket ignoreres
    hvis len (l) <= 1:
                  retur [l]
    r = []
    for i inden for rækkevidde (len (l)):
             s = l [: i] + l [i + 1:] # Indrykningsniveau valgt
             p = perm (er) # Skal have samme niveau som ovenfor
             for x i p:
              r.append (l [i: i + 1] + x) # Én plads okay
    returnere r

Der er også et par reserverede identifikatorer.

  • _ til import: funktioner / variabler startende med _ importeres ikke.
  • __ * __ til systemdefinerede navne, defineret ved implementering: vi har set et par af disse. (__str __ (), __iter __ (), __tilføjet __ ())

Python tilbyder også implicit streng bogstavelig sammenkædning

>>> def navn ():
... returner "Neil" "Kakkar"
...
>>> navn ()
'Neil Kakkar'

Formater strenge

Strengformatering er et nyttigt værktøj i Python.

Strenge kan have {expr} i den streng bogstavelige, hvor expr er et udtryk. Udtrykevalueringen erstattes på plads.

Konverteringer kan specificeres for at konvertere resultatet før formatering.

! r kalder repr (),! s opkald str () og! a ringer ascii ()

>>> navn = "Fred"
>>> f "Han sagde, at hans navn er {navn! r}."
"Han sagde, at hans navn er 'Fred'."
>>> f "Han sagde, at hans navn er {repr (navn)}." # repr () er ækvivalent. til! r
"Han sagde, at hans navn er 'Fred'."
>>> bredde = 10
>>> præcision = 4
>>> værdi = decimal.Decimal ("12.34567")
>>> f "resultat: {værdi: {bredde}. {præcision}}" # indlejrede felter
'resultat: 12.35'
# Dette er det samme som "{decf: 10.4f}". Format (decf = float (værdi))
>>> i dag = datetime (år = 2017, måned = 1, dag = 27)
>>> f "{i dag:% B% d,% Y}" # ved hjælp af datoformatspecifikation
'27. januar 2017'
>>> nummer = 1024
>>> f "{nummer: # 0x}" # ved hjælp af heltalformatspecifikator
'0x400'

Det er en renere syntaks til at bruge str.format ()

Resumé

Med dette har vi dækket de største søjler i Python. Objektdatamodellen, eksekveringsmodellen med dens scopes og blokke og nogle bits på strenge. Når du kender alt dette, ligger du foran enhver udvikler, der kun kender syntaks. Det er et højere antal, end du tror.

I del 2 ser vi på objektbaserede klasser og funktioner.

For at lære mere er her en fantastisk bog - Effektiv Python.
[Affiliate link - tak for at støtte!]

Andre historier i denne serie:

  • Hvordan man ikke længere er bange for Vim
  • Hvordan man ikke længere er bange for GIT

Nød du dette? Gå ikke glip af et indlæg igen - abonner på min adresseliste!