Gevorderde Python: Wat zijn magische methodes?

Dit artikel belicht de must-know Python speciale methoden die elke Python programmeur vertrouwd moet zijn

Farhad Malik

Follow

16 mei, 2020 – 14 min read

De magische methoden helpen ons om onze applicaties te verrijken. Ze voegen indirect magie toe aan onze Python code. Dit is een onderwerp voor gevorderden voor Python ontwikkelaars en ik raad het iedereen aan die de programmeertaal Python gebruikt of van plan is te gaan gebruiken.

De magic methods geven ons meer controle over hoe onze applicatie zich gedraagt.

Dit artikel is bedoeld om uit te leggen wat magic methods zijn en hoe ze gebruikt kunnen worden om Python applicaties te bouwen. Het zal een overzicht geven van de meest gebruikte magische methoden voor een reeks gegevenstypen.

Het doel van het schetsen van de belangrijkste magische methoden is voor ons om te begrijpen of we deze methoden willen overschrijven in onze aangepaste klassen om de toepassingen te verrijken.

Magische methoden maken de programmeertaal Python bijzonder krachtig

Foto door Rodion Kutsaev op Unsplash

Wat zijn de magische methoden van Python?

Python magische methoden zijn ook bekend als speciale methoden of dunder methoden. Ze worden omgeven door dubbele underscores, bijv. __init__().

Een object kan een aantal magische methoden hebben.

Bedenk dat alles in Python een object is, inclusief een variabele/functie/klasse enz. De objecten zijn Pythons abstractie voor gegevens.

De magische methoden worden gebruikt om nieuwe objecten te maken en te initialiseren, ze helpen ons een object op te halen als een woordenboek, ze worden gebruikt om een object te verwijderen, naast andere operaties. Ze worden gebruikt wanneer de + operator wordt aangeroepen, of zelfs wanneer we een object willen weergeven als een string.

Hoewel elke methode in Python publiek is, is de coderingsconventie om alle private methoden te omringen met dubbele underscores __<method>__()

Dit impliceert dat magic methods bedoeld zijn als private methoden. Het betekent ook dat de aanroeper van een object de methode niet rechtstreeks mag aanroepen, omdat de methode bedoeld is om te worden aangeroepen door de klasse die de magische methode intern heeft.

We kunnen de magische methoden overriden om onze eigen aangepaste functionaliteit te bieden.

Foto door Cristian Escobar op Unsplash

Ik zal de concepten van magische methoden uitleggen door een aangepaste klasse te maken en vervolgens zal ik een overzicht geven van de belangrijkste magische methoden in een geheel getal, een string, een lijst en een woordenboek.

Naarmate we verder komen in het artikel, zal het een stuk duidelijker worden waarom de magische methoden bestaan en hoe je ze moet gebruiken.

Als u de programmeertaal Python van beginner tot gevorderde wilt begrijpen, raad ik u het onderstaande artikel ten zeerste aan:

Ik zal het onderwerp magische methoden beginnen met het maken van een aangepaste klasse en vervolgens zal ik uitleggen hoe de magische methoden worden gebruikt.

De usecase om magische methoden te begrijpen

In het onderstaande knipsel heb ik een klasse gemaakt met de naam Human en vervolgens heb ik een instantie van de klasse Human geïnstantieerd.

Dit stukje code zal worden gebruikt om ons te helpen magische methoden te begrijpen.

Noteer, de id van het object mens is een geheel getal, de naam attribuut is van het type string, de eigenschap adressen is van het type lijst en de eigenschap kaarten is van het type woordenboek.

Ik heb de magische methoden gegroepeerd in verschillende gegevenstype secties voor het gemak van het lezen. Dezelfde magische methoden worden echter in verschillende gegevenstypen aangetroffen.

De magische methoden kunnen worden overschreven om de functionaliteit te verrijken en aangepaste logica te creëren die het beste bij de zakelijke behoeften past.

Class:

Laten we de magische methoden begrijpen die zijn gekoppeld aan het object mens. Als ik de methode dir(human) uitvoer, worden alle functies en attribuutnamen van het menselijke object opgesomd.

Er zijn in totaal 29 methoden/attributen die zijn gekoppeld aan het menselijke object. Daarvan zijn er 26 de magische methoden.

Dat is een vrij groot aantal speciale methoden. Deze methoden worden geërfd van het basistype van de Mens-klasse. Het zijn dus de voorgedefinieerde methoden die we kunnen gebruiken/overschrijven om de klassen te verrijken.

Het volgende deel van de sectie zal de belangrijkste magische methoden uitleggen.

__delattr__

Deze methode wordt aangeroepen wanneer we proberen een attribuut uit een klasse te verwijderen.

We kunnen de functionaliteit overriden door de methode in de klasse Human te implementeren:

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

Wanneer we nu een attribuut proberen te verwijderen, zal de melding verschijnen: Attribuut verwijderen

Er is ook de methode __setattr__() om een waarde aan een attribuut toe te kennen en __getattr__() om een waarde van het attribuut te krijgen.

Een usecase van__delattr__() kan zijn om te voorkomen dat bepaalde attributen van een object worden verwijderd of wanneer we specifieke acties willen uitvoeren wanneer een bepaald attribuut wordt verwijderd.

__dict__

Deze methode retourneert een woordenboek dat het object vertegenwoordigt. De sleutels van het woordenboek zijn de attributen van het object en de waarden zijn de waarden van de attributen.

Als een instantie:

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

De bovenstaande code retourneert:

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

__dir__

We kunnen de dir() methode overriden door de __dir__() methode in de klasse te overriden. Als voorbeeld kunnen we de interne methoden verwijderen uit het resultaat dat wordt geretourneerd door de dir() methode:

__eq__

Deze methode wordt aangeroepen wanneer we proberen de == operatie uit te voeren. Laten we bedenken dat twee menselijke objecten gelijk zijn wanneer hun id-attribuut gelijk is, ook al is hun naam verschillend.

We kunnen de methode __eq__() overriden om deze functionaliteit te bereiken:

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

Dit zal nu True teruggeven:

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

__format__

Wanneer we proberen string.format(), wordt de methode __format__() intern aangeroepen.

__ge__

Als voorbeeld, laten we aannemen dat er twee Mens-objecten zijn:

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

Laten we ook bedenken dat ons project een regel heeft dat een mens-object met een groter Id als groter wordt beschouwd dan het andere mens-object. Daarom kunnen we de methode __gt__() overriden en de aangepaste logica implementeren:

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

Dit zal nu False teruggeven omdat het id van het tweede menselijke object groter is dan het eerste menselijke object:

print(first >= second)
Returns False

Er is ook een methode __lt__() die wordt uitgevoerd wanneer de operator ≤ wordt uitgevoerd.

__hash__

Hashing wordt gebruikt om een object om te zetten in een geheel getal. Hashing wordt uitgevoerd wanneer we een item in een dictionary/set proberen te zetten.

Een goed hashing-algoritme resulteert in een lager aantal hashing-botsingen. We kunnen ons eigen hash-algoritme verschaffen door de __hash__() methode te overschrijven.

Laten we eens bedenken dat de id van het object Human uniek moet zijn in onze toepassing. Het __hash__() algoritme kan worden overschreven om het self.id terug te geven als het hash-gehele getal:

def __hash__(self):
return self.id

We kunnen twee objecten maken en ze opslaan in een verzameling sets. Wanneer we de lengte van de verzameling opvragen, zullen we twee elementen in de verzameling verwachten omdat beide objecten een verschillend id hebben.

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

Als we nu het id voor beide objecten op 1 zetten en de oefening herhalen, dan zullen we slechts 1 element in de verzameling zien omdat beide objecten dezelfde hash-sleutel hebben omdat hun id-attribuut hetzelfde is, ook al is hun naam-attribuut verschillend.

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

__init__

De methode __init__() wordt uitgevoerd wanneer we een nieuwe instantie van een klasse willen instantiëren door de constructor ervan aan te roepen.

Toen we een instantie probeerden uit te voeren:

human = Human(1, 'farhad')

Dan werd de __init__() methode uitgevoerd.

We kunnen de functionaliteit overriden en ook onze eigen aangepaste argumenten en gedrag erin doorgeven.

Als voorbeeld is de __init__() methode van de Mens-klasse:

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

Photo by Cederic X on Unsplash

__init_subclass__

Dit is een van de metaclass usecases. Wanneer een klasse wordt gesubklassed en het object ervan wordt aangemaakt, wordt de methode __init_subclass__() aangeroepen.

In wezen informeert de methode de ouder dat de klasse is gesubklassed. Deze haak kan dan alle subklassen van een bepaalde klasse initialiseren. De methode wordt dus gebruikt om subklassen te registreren en standaardwaarden toe te kennen aan de attributen van de subklassen. Zo kunnen we de initialisatie van subklassen aanpassen.

Ik zal in mijn volgende artikel uitleggen hoe metaclasses werken.

__new__

Wanneer we een nieuwe instantie van een klasse willen instantiëren/creëren dan wordt de methode __new__(cls) uitgevoerd.

Nemen we als voorbeeld dat we willen afdrukken ‘Human creating…’ telkens wanneer de constructor Human() wordt aangeroepen.

We kunnen de functionaliteit van de methode __new__(cls) overriden, zoals hieronder wordt getoond:

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

Als resultaat wordt Human creating… afgedrukt wanneer we een instantie probeerden te maken:

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

__sizeof__

Deze methode wordt aangeroepen wanneer we sys.getsizeof() uitvoeren. Het geeft de grootte van het object in het geheugen terug, in bytes.

__str__

Het drukt af: id=1. name=Farhad.

De __str__() functie moet proberen een gebruikersvriendelijke weergave van het object terug te geven.

__weakref__

Dit __weakref__ object geeft de lijst met zwakke verwijzingen naar het doelobject terug. In wezen helpt het de vuilnisophaaldienst om zwakke referenties te informeren dat de referent is opgehaald. Daarom voorkomt het dat de objecten toegang krijgen tot de onderliggende pointers.

Foto door Yousef Espanioly op Unsplash

Integer

Dit brengt ons bij het volgende deel van het artikel. We kunnen zien dat de id eigenschap van het object human van het type int is. Als we vervolgens dir() uitvoeren op de id eigenschap en de methoden eruit filteren die omgeven zijn door dubbele underscores, zullen we tegenkomen dat er in totaal 62 magische methoden zijn.

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

Ik zal de belangrijkste methoden hier uitleggen:

__add__

Deze methode wordt aangeroepen wanneer we twee getallen proberen op te tellen.

Als voorbeeld:

human.id + 2 is hetzelfde als human.id.__add__(2)

__en__

Deze methode wordt uitgevoerd wanneer we proberen de &-operator te gebruiken, bijv.b.:

return self & another_value

__bool__

Deze methode wordt uitgevoerd wanneer we proberen de booleaanse controle uit te voeren op een object b.v..

self != 123

__floordiv__

Deze methode wordt uitgevoerd wanneer we de //operator uitvoeren.

Foto door Johannes Plenio op Unsplash

__getnewargs__

Occasioneel picklen we objecten in Python. Met picklen wordt een byte-streamrepresentatie van een object in het geheugen gemaakt, die indien nodig op schijf kan worden opgeslagen.

De methode __getnewargs__() informeert het pickling-proces over hoe het het ingepickelde object moet terugladen om het doelobject opnieuw te maken. In het bijzonder hoe het object moet worden aangemaakt door de argumenten in te voeren in de new() methode. Vandaar de naam ‘get new args’.

__index__

Vervolgens kan een object worden omgezet in een geheel getal door de methode __index__() uit te voeren. We kunnen de methode ook overriden en onze eigen functionaliteit geven van hoe de index moet worden gegenereerd.

__invert__

Deze methode wordt uitgevoerd wanneer we de ~ operator gebruiken.

Als een instantie:

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

Het is hetzelfde als het uitvoeren van:

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

__lshift__

Deze methode geeft ons links verschuiving van een waarde b.v. zelf << waarde. We kunnen de << operator overladen door de __lshift__() methode te overschrijven.

Note: __rshift__() wordt uitgevoerd wanneer we de >> operator uitvoeren.

__mod__

Deze methode wordt aangeroepen wanneer we de % operator gebruiken.

__neg__

Deze methode wordt uitgevoerd als we de negatieve – operator gebruiken b.v..

first.id — second.id

__subclasshook__

Deze methode kan worden overruled om de issubclass() methode aan te passen. In essentie geeft het True terug als een klasse een subklasse is en False als het dat niet is. De methode retourneert ook NotImplemented, waardoor het bestaande algoritme kan worden gebruikt.

Deze methode kan het resultaat van de issubclass() methode aanpassen.

Photo by Patrick Selin on Unsplash

String

Dit brengt ons bij het volgende deel van het artikel.

We kunnen zien dat de eigenschap name van het object human van het type string is. Als we vervolgens dir(human.name) uitvoeren op de eigenschap en de methoden eruit filteren die zijn omgeven door dubbele underscores, zullen we zien dat er in totaal 33 magische methoden zijn.

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

Ik zal de 4 belangrijkste methoden hier uitleggen, omdat de rest van het artikel de meeste magische methoden al heeft uitgelicht.

__contains__

Deze methode wordt uitgevoerd wanneer we proberen te controleren of een bepaald teken bestaat.

__len__

Deze methode retourneert de lengte van de string. Ze wordt uitgevoerd wanneer we de len() methode uitvoeren.

Als we alleen specifieke tekens willen tellen om de lengte van de string te berekenen, dan kan de methode __()__ worden overschreven om die functionaliteit te bieden.

__repr__

Deze methode wordt uitgevoerd wanneer we een ontwikkelaar-vriendelijke representatie van een object willen maken.

Note, de __repr__ wordt uitgevoerd wanneer we print(object) als we geen __str__() implementatie in onze klasse hebben.

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

Dit zal afdrukken:

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

De methode __repr__() moet bedoeld zijn om de officiële weergave van een object te produceren.

__iadd__

Occasioneel gebruiken we een opteloperator naast de toewijzing e.g.

self.id += 1

Dit is equivalent aan self.id = self.id + 1

De methode iadd() wordt uitgevoerd wanneer we de optelling uitvoeren met de toewijzing.

Verder wordt de methode __ipow__() uitgevoerd wanneer **= wordt uitgevoerd.

De magische methode __iand__() is voor het uitvoeren van een bitwise AND met toewijzing en __ior__() wordt aangeroepen wanneer we proberen != te doen, zoals: i != j

Lijst

Dit brengt ons bij het volgende deel van het artikel. De eigenschap adressen van het object mens is van het type lijst. Als we vervolgens dir(human.addresses) uitvoeren op de addresses eigenschap en de methoden eruit filteren die omgeven zijn door dubbele underscores, zullen we tegenkomen dat er in totaal 35 magische methoden zijn.

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

Ik zal de belangrijkste methoden hier uitleggen:

__reduce__

Wanneer een object wordt ingemaakt, wordt de methode __reduce__() uitgevoerd om een object terug te geven dat de inmaker helpt te begrijpen hoe het terug te construeren.

__reduce_ex__

De methode __reduce_ex__() wordt door de pickle verkozen boven de methode __reduce__().

De methode __reduce_ex__() neemt een integer argument dat de protocolversie is. Het biedt achterwaartse compatibiliteit voor pickling en wordt gebruikt om de gepickte byte-stream naar het object te construeren.

__reversed__

De methode __reversed__() wordt uitgevoerd wanneer we een collectie in de omgekeerde volgorde proberen om te keren.

Het wordt uitgevoerd wanneer de methode reversed(collection) of collection.reverse() wordt aangeroepen. Soms besluiten we de functionaliteit van de methode reversed() te wijzigen.

Door de methode __reversed__() te overriden, kunnen we het gewenste resultaat bereiken.

Dictionary

Dit brengt ons bij de vijfde sectie van het artikel.

Dictionary is een van de belangrijkste builtin types in Python. Als we dir(human.maps) uitvoeren en de methoden eruit filteren die omgeven zijn door dubbele underscores, zullen we tegenkomen dat er in totaal 29 magische methoden zijn.

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

Van de 29 magische methoden, zal ik hier de 4 belangrijkste methoden uitleggen:

__delitem__

Deze methode wordt uitgevoerd wanneer we een item verwijderen, bijv.g.

del dictionary

__getitem__

Deze methode wordt uitgevoerd wanneer we een item voor een sleutel proberen te krijgen:

item = dictionary

__setitem__

Deze methode wordt uitgevoerd als we een item in het woordenboek proberen in te stellen:

dictionary = item

__iter__

Deze methode retourneert een iterator voor de verzameling. Een iterator helpt ons om over een verzameling te itereren.

We kunnen overriden hoe de iterator() wordt uitgevoerd door de __iter__() methode te overriden.

Tot slot nog een opmerking over __call__()

Wat als we ons object oproepbaar willen maken? Stel dat we van het object Human een aanroepbare functie Human() willen maken?

De methode __call__() stelt ons in staat om de klassen als functies te behandelen.

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

We kunnen deze functionaliteit bereiken door de __call__() magische methode-implementatie in onze klasse Human op te nemen.

Dit zal afdrukken:

U probeerde
Argumenten aan te roepen: ()
Keyword Arguments: {}
id=1 (<klasse ‘int’>). name=Farhad (<klasse ‘str’>)
Aanroep voltooid

Door de methode __call__() te overschrijven, kunnen we nu een decorator implementeren om een object als functie te retourneren of zelfs die bibliotheken aan te roepen die functies als argumenten accepteren door de eigenlijke objecten in te voeren.

Foto door Dollar Gill op Unsplash

Samenvatting

Dit is een gevorderd niveau onderwerp voor Python ontwikkelaars en ik raad het iedereen aan die de Python programmeertaal gebruikt of van plan is dat te gaan doen.

Dit artikel was bedoeld om uit te leggen wat magic methods zijn en hoe ze kunnen worden gebruikt om Python applicaties te bouwen. Het geeft een overzicht van de meest gebruikte magic methods in een custom class, integers, strings, lists, en dictionary data types.

Hoewel elke methode in Python publiek is, is de coderingsconventie om alle private methods te omringen met dubbele underscores __<method>__()

Dit impliceert dat magic methods bedoeld zijn om private methods te zijn. Het betekent ook dat de aanroeper van een object de methode niet direct mag aanroepen en dat de methode wordt aangeroepen door de klasse intern die de magic method heeft. Magic methods geven ons meer controle over hoe onze applicatie zich gedraagt.

De magic methods kunnen worden overschreven om de functionaliteit te verrijken en aangepaste logica te creëren die het beste past bij de zakelijke behoeften.

Het doel van het schetsen van de belangrijkste magic methods is voor ons om te begrijpen of we deze methods willen overschrijven in onze aangepaste klassen om de applicaties te verrijken.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.