Denne artikel fremhæver de must-know Python-specialmetoder, som enhver Python-programmør skal kende
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
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.
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
__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.
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.
__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.
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.