Advanced Python: Hvad er magiske metoder?

Denne artikel fremhæver de must-know Python-specialmetoder, som enhver Python-programmør skal kende

Farhad Malik

Follow

16. maj, 2020 – 14 min læsning

De magiske metoder hjælper os med at berige vores applikationer. De tilføjer indirekte magi til vores Python-kode. Dette er et emne på avanceret niveau for Python-udviklere, og jeg anbefaler det til alle, der er/eller har til hensigt at bruge programmeringssproget Python.

De magiske metoder giver os mere kontrol over, hvordan vores applikation opfører sig.

Denne artikel har til formål at forklare, hvad magiske metoder er, og hvordan de kan bruges til at opbygge Python-applikationer. Den vil give en oversigt over de mest udbredte magiske metoder for en række datatyper.

Sigtet med at skitsere de vigtigste magiske metoder er, at vi skal forstå, om vi ønsker at overskrive disse metoder i vores brugerdefinerede klasser for at berige applikationerne.

Magiske metoder gør programmeringssproget Python ekstremt kraftfuldt

Foto af Rodion Kutsaev på Unsplash

Hvad er de magiske metoder i Python?

Python magiske metoder er også kendt som specielle metoder eller dundermetoder. De er omgivet af dobbelte understregninger f.eks. __init__().

Et objekt kan have et antal magiske metoder.

Husk, at alt i Python er et objekt, herunder en variabel/funktion/klasse osv. Objekterne er pythons abstraktion for data.

De magiske metoder bruges til at konstruere og initialisere nye objekter, de hjælper os med at hente et objekt som en ordbog, de bruges til at slette et objekt blandt andre operationer. De bruges, når operatoren + påkaldes, eller endda når vi ønsker at repræsentere et objekt som en streng.

Selv om alle metoder i Python er offentlige, er kodningskonventionen at omgive alle private metoder med dobbelte understregninger __<method>__()

Dette indebærer, at magiske metoder er beregnet til at være private metoder. Det betyder også, at den, der kalder et objekt, ikke skal påkalde metoden direkte, da det er meningen, at metoden skal påkaldes af den klasse internt, der har den magiske metode.

Vi kan overskrive de magiske metoder for at give vores egen brugerdefinerede funktionalitet.

Foto af Cristian Escobar på Unsplash

Jeg vil forklare koncepterne for magiske metoder ved at oprette en brugerdefineret klasse, og derefter vil jeg give en oversigt over de vigtigste magiske metoder i et heltal, en streng, en liste og en ordbog.

Når vi kommer længere ned i artiklen, vil den begynde at opbygge et meget klarere billede af, hvorfor de magiske metoder findes, og hvordan man bruger dem.

Hvis du ønsker at forstå programmeringssproget Python fra begynder- til avanceret niveau, så kan jeg varmt anbefale nedenstående artikel:

Jeg vil starte emnet magiske metoder ved at oprette en brugerdefineret klasse, og derefter vil jeg forklare, hvordan de magiske metoder bruges.

Brugssagen til forståelse af magiske metoder

I nedenstående uddrag har jeg oprettet en klasse kaldet Human, og derefter har jeg instantieret en instans af Human-klassen.

Dette uddrag af kode vil blive brugt til at hjælpe os med at forstå magiske metoder.

Bemærk, at id for objektet human er af typen heltal, attributten name er af typen string, egenskaben addresses er af typen list og egenskaben maps er af typen dictionary.

Jeg har grupperet de magiske metoder i forskellige datatypeafsnit for at gøre det lettere at læse. De samme magiske metoder findes dog i forskellige datatyper.

De magiske metoder kan overskrives for at berige funktionaliteten og skabe brugerdefineret logik, der passer bedst til forretningsbehovene.

Klasse:

Lad os forstå de magiske metoder, der er knyttet til det menneskelige objekt. Hvis jeg udfører dir(human)-metoden, vil den opstille en liste over alle funktioner og attributnavne for det menneskelige objekt.

Der er i alt 29 metoder/attributter, der er knyttet til det menneskelige objekt. Blandt dem er 26 af dem de magiske metoder.

Det er et ret stort antal specielle metoder. Disse metoder er arvet fra basistypen for klassen Human. Derfor er de de foruddefinerede metoder, som vi kan bruge/override for at berige klasserne.

Den næste del af afsnittet vil forklare de vigtigste magiske metoder.

__delattr__

Denne metode kaldes, når vi forsøger at slette en attribut fra en klasse.

Vi kan overskrive funktionaliteten ved at implementere metoden i Human-klassen:

def __delattr__(self, item):
print('Deleting attribute ' + item)
return object.__delattr__(self, item)

Nu, når vi forsøger at slette en attribut, vises meddelelsen: Sletning af attribut

Der er også metoden __setattr__() til at tildele en værdi til en attribut og __getattr__() til at hente en værdi fra attributten.

Et usecase af__delattr__() kan være at forhindre, at bestemte attributter på et objekt slettes, eller når vi ønsker at udføre bestemte handlinger, når en bestemt attribut slettes.

__dict__

Denne metode returnerer en ordbog, der repræsenterer objektet. Nøglerne i ordbogen er objektets attributter, og værdierne er værdierne af attributterne.

Som instans:

human = Human(2, 'Malik').__dict__
print(human)

Koden ovenfor returnerer:

{‘id’: 2, ‘name’: ‘Malik’, ‘addresses’: , ‘maps’: {}}

__dir__

Vi kan overskrive dir()-metoden ved at overskrive __dir__()-metoden i klassen. Som eksempel kan vi fjerne de interne metoder fra det resultat, der returneres af dir()-metoden:

__eq__

Denne metode kaldes, når vi forsøger at udføre ==-operationen. Lad os overveje, at to menneskelige objekter er ens, når deres id-attribut er ens, selv om deres navn er forskelligt.

Vi kan overskrive __eq__()-metoden for at opnå denne funktionalitet:

def __eq__(self, other):
return self.id == other.id

Dette vil nu returnere True:

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
print(first == second)

__format__

Når vi forsøger at udføre string.format(), påkaldes __format__()-metoden internt.

__ge__

Som eksempel antager vi, at der er to Human-objekter:

first = Human(1, 'Farhad')
second = Human(2, 'Malik')

Lad os også overveje, at vores projekt har en regel om, at et Human-objekt med et større Id betragtes som større end det andet Human-objekt. Derfor kan vi overskrive __gt__()-metoden og implementere den brugerdefinerede logik:

def __ge__(self, other):
return self.id >= other.id

Dette vil nu returnere False, fordi id’et for det andet menneskeobjekt er større end det første menneskeobjekt:

print(first >= second)
Returns False

Der er også en __lt__()-metode, som udføres, når operatoren ≤ udføres.

__hash__

Hashing bruges til at konvertere et objekt til et heltal. Hashing udføres, når vi forsøger at indstille et element i en ordbog/et sæt.

En god hashing-algoritme resulterer i et lavere antal hashing-kollisioner. Vi kan levere vores egen hash-algoritme ved at overskrive __hash__()-metoden.

Lad os overveje, at id’et for Human-objektet skal være unikt i vores applikation. __hash__()-algoritmen kan overskrives for at returnere self.id som hash-integritetstal:

def __hash__(self):
return self.id

Vi kan oprette to objekter og gemme dem i en sætopsamling. Når vi spørger om længden af sættet, vil vi forvente to elementer i sættet, fordi begge objekter har et forskelligt id.

first = Human(1, 'Farhad')
second = Human(2, 'Malik')
my_set = set()
print(len(my_set))
#returns 2

Hvis vi nu sætter id til at være 1 for begge objekter og gentager øvelsen, vil vi kun se 1 element i sættet, fordi begge objekter har den samme hash-nøgle, da deres id-attribut er den samme, selv om deres name-attribut er forskellig.

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
my_set = set()
print(len(my_set))
#returns 1

__init__

Metoden __init__() udføres, når vi ønsker at instantiere en ny instans af en klasse ved at kalde dens konstruktør.

Som instans, da vi forsøgte at udføre:

human = Human(1, 'farhad')

Derpå blev __init__()-metoden udført.

Vi kan overskrive funktionaliteten og indsætte vores egne brugerdefinerede argumenter og adfærd i den også.

Som eksempel er __init__()-metoden for klassen Human:

def __init__(self, id, name, addresses=, maps={}):
self.id = id
self.name = name
self.addresses = addresses
self.maps = maps

Foto af Cederic X på Unsplash

__init_subclass__

Dette er et af metaklassens brugstilfælde. Når en klasse underklasses, og dens objekt oprettes, kaldes metoden __init_subclass__().

Metoden informerer i bund og grund den overordnede klasse om, at den er blevet underklasset. Denne hook kan derefter initialisere alle underklasser af en given klasse. Derfor bruges metoden til at registrere underklasser og til at tildele standardværdier til attributterne på underklasserne. Derfor giver den os mulighed for at tilpasse initialiseringen af underklasser.

Jeg vil forklare, hvordan metaklasser fungerer i min næste artikel.

__new__

Når vi ønsker at instantiere/skabe en ny instans af en klasse, udføres __new__(cls)-metoden.

Som eksempel kan vi antage, at vi ønsker at udskrive “Human creating…”, hver gang konstruktøren Human() kaldes.

Vi kan overskrive funktionaliteten af __new__(cls)-metoden som vist nedenfor:

def __new__(cls, *args, **kwargs):
print('Human creating...')
return object.__new__(cls)

Som følge heraf udskrives Human creating…, når vi har forsøgt at oprette en instans:

human = Human(1, 'Farhad', , {'London':2, 'UK':3})

__sizeof__

Denne metode kaldes, når vi udfører sys.getsizeof(). Den returnerer objektets størrelse i hukommelsen i bytes.

__str__

Den udskrev: id=1. name=Farhad.

Funktionen __str__() skal forsøge at returnere en brugervenlig repræsentation af objektet.

__weakref__

Dette __weakref__-objekt returnerer listen over svage referencer til målobjektet. I det væsentlige hjælper det garbage collection med at informere svage referencer om, at referenten er blevet indsamlet. Derfor forhindrer det objekterne i at få adgang til de underliggende pointere.

Foto af Yousef Espanioly på Unsplash

Integer

Dette bringer os til næste afsnit i artiklen. Vi kan se, at id-egenskaben for objektet human er af typen int. Hvis vi derefter udfører dir() på id-egenskaben og filtrerer de metoder, der er omgivet af dobbelte understregninger, vil vi støde på, at der i alt er 62 magiske metoder.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.id)))))

Jeg vil forklare de vigtigste metoder her:

__add__

Denne metode kaldes, når vi forsøger at addere to tal.

Som eksempel:

human.id + 2 er det samme som human.id.__add__(2)

__og__

Denne metode udføres, når vi forsøger at bruge &-operatoren e.g.:

return self & another_value

__bool__

Denne metode udføres, når vi forsøger at udføre den boolske kontrol af et objekt e.g.

self != 123

__floordiv__

Denne metode udføres, når vi udfører //operatoren.

Foto af Johannes Plenio på Unsplash

__getnewargs__

Fiktionsvis pille vi objekter i Python. Pickling skaber en byte-stream-repræsentation af et objekt i hukommelsen, som kan gemmes på disk, hvis det er nødvendigt.

Metoden __getnewargs__() informerer pickling-processen om, hvordan den skal indlæse det picklede objekt tilbage for at genskabe målobjektet. Især hvordan objektet skal oprettes ved at indsætte argumenterne til new()-metoden. Derfor navnet ‘get new args’.

__index__

Derpå kan et objekt konverteres til et heltal ved at udføre __index__()-metoden. Vi kan også overskrive metoden og give vores egen funktionalitet for, hvordan indekset skal genereres.

__invert__

Denne metode udføres, når vi bruger ~-operatoren.

Som instans:

first = Human(1, 'Farhad')
print(~first.id)

Det er det samme som at udføre:

first = Human(1, 'Farhad')
print(first.id.__invert__())

__lshift__

Denne metode giver os venstreforskydning af en værdi, f.eks. self << værdi. Vi kan overloade <<-operatoren ved at overskrive __lshift__()-metoden.

Bemærk: __rshift__() udføres, når vi udfører >>-operatoren.

__mod__

Denne metode kaldes, når vi bruger %-operatoren.

__neg__

Denne metode udføres, når vi bruger den negative –operator, f.eks.

first.id — second.id

__subclasshook__

Denne metode kan overskrives for at tilpasse issubclass()-metoden. Den returnerer i bund og grund True, hvis en klasse er en underklasse, og False, hvis den ikke er det. Metoden returnerer også NotImplemented, hvilket gør det muligt at bruge den eksisterende algoritme.

Metoden kan tilpasse resultatet af issubclass()-metoden.

Foto af Patrick Selin på Unsplash

String

Dette bringer os til næste afsnit i artiklen.

Vi kan se, at name-egenskaben for objektet human er af typen string. Hvis vi derefter udfører dir(human.name) på egenskaben og filtrerer de metoder, der er omgivet af dobbelte understregninger, vil vi bemærke, at der i alt er 33 magiske metoder.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.name)))))

Jeg vil forklare de 4 vigtigste metoder her, da resten af artiklen allerede har fremhævet de fleste af de magiske metoder.

__contains__

Denne metode udføres, når vi forsøger at kontrollere, om et givet tegn eksisterer.

__len__

Denne metode returnerer strengenes længde. Den udføres, når vi udfører len()-metoden.

Hvis vi kun ønsker at tælle bestemte tegn for at beregne strengen længde, kan metoden __()__ overskrives for at give denne funktionalitet.

__repr__

Denne metode udføres, når vi ønsker at oprette en udviklervenlig repræsentation af et objekt.

Bemærk, at __repr__ udføres, når vi printer(objekt), hvis vi ikke har en __str__()-implementering i vores klasse.

def __repr__(self):
return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'print(Human(1, 'Farhad'))

Dette vil udskrive:

id=1 (<klasse ‘int’>). name=Farhad (<klasse ‘str’>)

Metoden __repr__() bør være beregnet til at frembringe den officielle repræsentation af et objekt.

__iadd__

Formelt bruger vi en additionsoperator ved siden af tildelingen e.g.

self.id += 1

Dette svarer til self.id = self.id + 1

Metoden iadd() udføres, når vi udfører additionen med tildelingen.

Dertil kommer, at metoden __ipow__() udføres, når **= udføres.

Den magiske metode __iand__() bruges til at udføre bitvis AND med en tildeling, og __ior__() kaldes, når vi forsøger at lave != som f.eks. i != j

List

Dette bringer os til næste afsnit i artiklen. Ejendommen addresses for objektet human er af typen list. Hvis vi derefter udfører dir(human.addresses) på egenskaben addresses og filtrerer de metoder, der er omgivet af dobbelte understregninger, vil vi støde på, at der i alt er 35 magiske metoder.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.addresses)))))

Jeg vil forklare de vigtigste metoder her:

__reduce__

Når et objekt pickles, udføres __reduce__()-metoden for at returnere et objekt, der hjælper picklet med at forstå, hvordan det skal konstrueres tilbage.

__reduce_ex__

Metoden __reduce_ex__() foretrækkes af pickle frem for __reduce__()-metoden.

Metoden __reduce_ex__() tager et heltalsargument, som er protokolversionen. Den giver bagudkompatibilitet for pickle og bruges til at konstruere den pickleed byte-stream til objektet.

__reversed__

Metoden __reversed__() udføres, når vi forsøger at vende en samling i den omvendte rækkefølge.

Den udføres, når reversed(collection) eller collection.reverse() kaldes. Nogle gange beslutter vi os for at ændre funktionaliteten af reversed()-metoden.

Gennem at overskrive __reversed__()-metoden kan vi opnå det ønskede resultat.

Dictionary

Dette bringer os til artiklens femte afsnit.

Dictionary er en af de vigtigste indbyggede typer i Python. Hvis vi udfører dir(human.maps) og filtrerer de metoder, der er omgivet af dobbelte understregninger, vil vi støde på, at der i alt er 29 magiske metoder.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.maps)))))

Med hensyn til de 29 magiske metoder vil jeg her forklare de 4 vigtigste metoder:

__delitem__

Denne metode udføres, når vi sletter et element e.g.

del dictionary

__getitem__

Denne metode udføres, når vi forsøger at hente et element for en nøgle:

item = dictionary

__setitem__

Denne metode udføres, når vi forsøger at indstille et element i ordbogen:

dictionary = item

__iter__

Denne metode returnerer en iterator for samlingen. En iterator hjælper os med at iterere over en samling.

Vi kan overskrive, hvordan iterator() udføres, ved at overskrive __iter__()-metoden.

Sidst en bemærkning om __call__()

Hvad nu, hvis vi ville gøre vores objekt kaldbart? Lad os antage, at vi ønskede at gøre objektet menneske til en kaldbar human()-funktion?

Med __call__()-metoden kan vi behandle klasserne som funktioner.

human = Human(1, 'Farhad')
human()

Vi kan opnå denne funktionalitet ved at levere implementeringen af den magiske __call__()-metode i vores menneskeklasse.

Dette vil udskrive:

Du forsøgte at kalde
Argumenter: ()
Nøgleord Argumenter: {}
id=1 (<klasse ‘int’>). name=Farhad (<klasse ‘str’>)
Kald gennemført

Gennem at overskrive __call__()-metoden kan vi nu implementere en dekorator til at returnere et objekt som en funktion eller endda kalde de biblioteker, der accepterer funktioner som argumenter, ved at overgive de faktiske objekter.

Foto af Dollar Gill på Unsplash

Summary

Dette er et emne på avanceret niveau for Python-udviklere, og jeg anbefaler det til alle, der er/eller har til hensigt at bruge programmeringssproget Python.

Denne artikel har til hensigt at forklare, hvad magiske metoder er, og hvordan de kan bruges til at opbygge Python-applikationer. Den gav en oversigt over de mest anvendte magiske metoder i en brugerdefineret klasse, hele tal, strenge, lister og ordbogsdatatyper.

Selv om alle metoder i Python er offentlige, er kodningskonventionen at omgive alle private metoder med dobbelte understregninger __<method>__()

Dette indebærer, at magiske metoder er beregnet til at være private metoder. Det betyder også, at den, der kalder et objekt, ikke skal påberåbe sig metoden direkte, og at metoden påberåbes internt af den klasse, der har den magiske metode. Magiske metoder giver os mulighed for at få mere kontrol over, hvordan vores applikation opfører sig.

De magiske metoder kan overskrives for at berige funktionaliteten og skabe brugerdefineret logik, der passer bedst til forretningens behov.

Sigtet med at skitsere de vigtigste magiske metoder er, at vi skal forstå, om vi ønsker at overskrive disse metoder i vores brugerdefinerede klasser for at berige applikationerne.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.