Advanced Python: Python: Mitä ovat taikamenetelmät?

Tässä artikkelissa tuodaan esiin Pythonin erikoismetodit, jotka jokaisen Python-ohjelmoijan on tunnettava

Farhad Malik

Seuraa

16. toukokuuta, 2020 – 14 min lukea

Taikojen metodit auttavat meitä rikastuttamaan sovelluksia. Ne lisäävät epäsuorasti taikuutta Python-koodiimme. Tämä on edistyneen tason aihe Python-kehittäjille, ja suosittelen sitä kaikille, jotka käyttävät tai aikovat käyttää Python-ohjelmointikieltä.

Magiset metodit antavat meille enemmän kontrollia siihen, miten sovelluksemme käyttäytyy.

Tämän artikkelin tarkoituksena on selittää, mitä maagiset metodit ovat ja miten niitä voidaan käyttää Python-sovellusten rakentamisessa. Se antaa yleiskatsauksen yleisimmin käytetyistä taikametodeista useille tietotyypeille.

Keskeisten taikametodien hahmottamisen tarkoituksena on, että voimme ymmärtää, haluammeko ohittaa nämä metodit mukautetuissa luokissamme sovellusten rikastuttamiseksi.

Magiset metodit tekevät Python-ohjelmointikielestä erittäin tehokkaan

Kuvaaja: Rodion Kutsaev on Unsplash

Mitkä ovat Pythonin magiset metodit?

Pythonin taikametodit tunnetaan myös nimellä erikoismetodit tai dunder-metodit. Niitä ympäröi kaksinkertainen alleviivaus esim. __init__().

Objektilla voi olla useita taikametodeja.

Muista, että kaikki Pythonissa on objekteja, mukaan lukien muuttuja/funktio/luokka jne. Objektit ovat Pythonin abstraktio tiedolle.

Magisia metodeja käytetään uusien objektien rakentamiseen ja alustamiseen, niiden avulla voimme hakea objektin sanakirjana, niitä käytetään muun muassa objektin poistamiseen. Niitä käytetään, kun kutsutaan +-operaattoria tai jopa silloin, kun haluamme esittää objektin merkkijonona.

Vaikka kaikki Pythonissa käytetyt metodit ovat julkisia, koodauskäytäntö on se, että kaikki yksityiset metodit ympäröidään kaksinkertaisilla alleviivausmerkinnöillä __<metodi>__()

Tämä viittaa siihen, että maagiset metodit on tarkoitettu yksityisiksi metodeiksi. Se tarkoittaa myös sitä, että objektin kutsujan ei pitäisi kutsua metodia suoraan, koska metodi on tarkoitettu kutsuttavaksi luokan sisäisesti, jolla on maaginen metodi.

Voidaan ohittaa maagiset metodit oman mukautetun toiminnallisuuden tarjoamiseksi.

Photo by Cristian Escobar on Unsplash

Esittelen maagisten metodien käsitteitä luomalla mukautetun luokan, minkä jälkeen annan yleiskatsauksen tärkeimpiin maagisiin metodeihin kokonaisluvun (integer), merkkijonon (string), luettelon (list) ja sanakirjan avulla.

Kun etenemme artikkelissa eteenpäin, siitä alkaa muodostua paljon selkeämpi kuva siitä, miksi taikametodit ovat olemassa ja miten niitä käytetään.

Jos haluat ymmärtää Python-ohjelmointikieltä aloittelijasta edistyneempään, niin suosittelen lämpimästi alla olevaa artikkelia:

Aloitan maagisten metodien aiheen luomalla mukautetun luokan ja sitten selitän, miten maagisia metodeja käytetään.

Käyttökohde maagisten metodien ymmärtämiseen

Alhaalla olevaan pätkään loin luokan nimeltä Ihminen (Human) ja sen jälkeen instanttisoin instanssin luokan Ihminen.

Tätä koodinpätkää käytetään apuna taikametodien ymmärtämisessä.

Huomaa, että olion human id on kokonaislukutyyppiä, name-attribuutti on tyypiltään merkkijono, omaisuus addresses on tyypiltään lista ja omaisuus maps on tyypiltään sanakirja.

Olen ryhmitellyt taikametodit eri tietotyyppejä käsitteleviin osioihin lukemisen helpottamiseksi. Samat taikametodit löytyvät kuitenkin eri tietotyypeistä.

Taikametodit voidaan ohittaa toiminnallisuuden rikastuttamiseksi ja mukautetun logiikan luomiseksi, joka vastaa parhaiten liiketoiminnan tarpeita.

Luokka:

Ymmärretäänpä taikametodit, jotka liittyvät ihmisobjektiin. Jos suoritan dir(human)-metodin, se listaa kaikki human-objektin funktiot ja attribuuttien nimet.

Menetelmiä/attribuutteja, jotka liittyvät human-objektiin, on yhteensä 29 kappaletta. Niistä 26 on taikametodeja.

Se on aika suuri määrä erikoismetodeja. Nämä metodit periytyvät Human-luokan perustyypistä. Näin ollen ne ovat valmiiksi määriteltyjä metodeja, joita voimme käyttää/ylittää luokkien rikastamiseksi.

Luvun seuraavassa osassa selitetään keskeiset maagiset metodit.

__delattr__

Tätä metodia kutsutaan, kun yritämme poistaa attribuutin luokasta.

Voitamme ohittaa toiminnallisuuden toteuttamalla metodin Human-luokassa:

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

Nyt aina kun yritämme poistaa attribuutin, se näyttää viestin: Deleting attribute

On olemassa myös metodi __setattr__(), jolla attribuutille annetaan arvo ja __getattr__(), jolla attribuutin arvo saadaan.

__delattr__():n käyttötapauksena voi olla, että halutaan estää objektin tiettyjen attribuuttien poistaminen tai kun halutaan suorittaa tiettyjä toimintoja, kun tietty attribuutti poistetaan.

__dict__

Tämä metodi palauttaa sanakirjan, joka esittää objektia. Sanakirjan avaimet ovat objektin attribuutteja ja arvot ovat attribuuttien arvoja.

Instanssina:

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

Yllä oleva koodi palauttaa:

{’id’: 2, ’name’: ’Malik’, ’addresses’: , ’kartat’: {}}

__dir__

Voimme ohittaa dir()-metodin ohittamalla luokan __dir__()-metodin. Esimerkkinä voimme poistaa sisäiset metodit dir()-metodin palauttamasta tuloksesta:

__eq__

Tätä metodia kutsutaan, kun yritämme suorittaa ==-operaation. Ajatellaan, että kaksi ihmisen objektia ovat samanarvoisia, kun niiden id-attribuutti on sama, vaikka niiden nimi olisi eri.

Voidaan ohittaa __eq__()-metodi tämän toiminnallisuuden aikaansaamiseksi:

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

Tämä palauttaa nyt True:

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

__format__

Kun yritämme tehdä merkkijonoa.format(), kutsutaan sisäisesti metodia __format__().

__ge__

Esimerkiksi oletetaan, että on olemassa kaksi Human-objektia:

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

Lasketaan myös, että projektissamme on sääntö, jonka mukaan suuremman Id:n omaava Human-objekti katsotaan suuremmaksi kuin toinen Human-objekti.

__hash__

Hashingia käytetään objektin muuntamiseen kokonaisluvuksi. Hashaus suoritetaan, kun yritämme asettaa kohteen sanakirjaan/joukkoon.

Hyvän hashausalgoritmin tuloksena on pienempi määrä hashauskollisioita. Voimme tarjota oman hash-algoritmimme ohittamalla __hash__()-metodin.

Ajatellaanpa, että Human-olion id:n on tarkoitus olla sovelluksessamme uniikki. __hash__()-algoritmi voidaan ohittaa palauttamaan self.id hash-kokonaislukuna:

def __hash__(self):
return self.id

Voidaan luoda kaksi objektia ja tallentaa ne joukko-kokoelmaan. Kun kysymme joukon pituutta, odotamme, että joukossa on kaksi elementtiä, koska molemmilla objekteilla on eri id.

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

Jos nyt asetamme id:n arvoksi 1 molemmille objekteille ja toistamme harjoituksen, näemme vain yhden elementin joukossa, koska molemmilla objekteilla on sama hash-avain, koska niiden id-attribuutti on sama, vaikka niiden name-attribuutti on eri.

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

__init__

Metodi __init__() suoritetaan, kun haluamme instansoida uuden instanssin luokasta kutsumalla sen konstruktoria.

Kun yritimme suorittaa instanssin:

human = Human(1, 'farhad')

Tällöin suoritettiin __init__()-metodi.

Voimme ohittaa toiminnallisuuden ja syöttää siihen myös omia mukautettuja argumenttejamme ja käyttäytymistä.

Esimerkiksi Human-luokan __init__()-metodi on:

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__

Tämä on yksi metaluokkien käyttötapauksista. Kun luokka aliluokitetaan ja sen objekti luodaan, kutsutaan metodia __init_subclass__().

Välttämättä metodi ilmoittaa vanhemmalle luokalle, että se on aliluokitettu. Tämä koukku voi sitten alustaa kaikki tietyn luokan alaluokat. Metodia käytetään siis aliluokkien rekisteröintiin ja oletusarvojen antamiseen aliluokkien attribuuteille. Näin ollen sen avulla voimme muokata alaluokkien alustusta.

Kerron metaluokkien toiminnasta seuraavassa artikkelissani.

__new__

Kun haluamme instansioida/luoda uuden instanssin luokasta, suoritetaan metodi __new__(cls).

Asettakaamme esimerkkinä, että haluamme tulostaa ’Human creating…’ aina kun Human()-konstruktoria kutsutaan.

Voidaan ohittaa __new__(cls)-metodin toiminnallisuus alla esitetyllä tavalla:

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

Tulostuu siis Human creating…, kun yritämme luoda instanssin:

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

__sizeof__

Metodia kutsutaan, kun suoritetaan sys.getsizeof(). Se palauttaa objektin koon muistissa tavuina.

__str__

Se tulostaa: id=1. name=Farhad.

Funktion __str__() pitäisi pyrkiä palauttamaan käyttäjäystävällinen esitys objektista.

__weakref__

Tämä __weakref__-objekti palauttaa listan kohdeobjektin heikoista viittauksista. Pohjimmiltaan se auttaa roskienkeräystä ilmoittamaan heikoille viitteille, että referenssi on kerätty. Siksi se estää objekteja pääsemästä käsiksi taustalla oleviin osoittimiin.

Kuva: Yousef Espanioly on Unsplash

Integeri

Tämä johdattaa seuraavaan osaan artikkelista. Näemme, että objektin human id-ominaisuus on tyyppiä int. Jos sitten suoritamme dir()-toiminnon id-ominaisuudelle ja suodatamme pois metodit, joita ympäröi kaksinkertainen alleviivaus, törmäämme siihen, että taikametodeja on yhteensä 62.

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

Esittelen tässä keskeiset metodit:

__add__

Tätä metodia kutsutaan, kun yritämme laskea yhteen kaksi lukua.

Esimerkiksi:

human.id + 2 on sama kuin human.id.__add__(2)

__and__

Tämä metodi suoritetaan, kun yritämme käyttää & operaattoria e.g.:

return self & another_value

__bool__

Tämä metodi suoritetaan, kun yritämme suorittaa boolean-tarkastuksen objektille esim.

self != 123

__floordiv__

Tämä metodi suoritetaan, kun suoritamme //operaattoria.

Photo by Johannes Plenio on Unsplash

__getnewargs__

Objekteja pikkelöidään Pythonissa toisinaan. Poiminta luo muistissa olevasta objektista tavuvirtamuotoisen esityksen, joka voidaan tarvittaessa tallentaa levylle.

Metodi __getnewargs__() ilmoittaa poimintaprosessille, miten poimittu objekti pitää ladata takaisin, jotta kohdeobjekti voidaan luoda uudelleen. Erityisesti se, miten objekti on luotava antamalla argumentit new()-metodille. Tästä johtuu nimi ’get new args’.

__index__

Sen jälkeen objekti voidaan muuntaa kokonaisluvuksi suorittamalla __index__()-metodi. Voimme myös ohittaa metodin ja tarjota oman toiminnallisuutemme siitä, miten indeksi on muodostettava.

__invert__

Tämä metodi suoritetaan, kun käytämme ~-operaattoria.

Instanssina:

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

Se on sama kuin suorittaisi:

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

__lshift__

Tämän metodin avulla saamme siirretyksi arvon vasemmalle esim. self <<-arvon. Voimme ylikuormittaa <<-operaattorin ohittamalla metodin __lshift__().

Huomautus: __rshift__() suoritetaan, kun suoritamme >>-operaattorin.

__mod__

Tätä metodia kutsutaan, kun käytämme % -operaattoria.

__neg__

Tämä metodi suoritetaan, kun käytämme negatiivista -operaattoria esim.

first.id — second.id

__subclasshook__

Tämä metodi voidaan ohittaa issubclass()-metodin mukauttamiseksi. Pohjimmiltaan se palauttaa True:n, jos luokka on aliluokka ja False:n, jos se ei ole. Metodi palauttaa myös NotImplemented, jolloin voidaan käyttää olemassa olevaa algoritmia.

Metodilla voidaan muokata issubclass()-metodin tulosta.

Photo by Patrick Selin on Unsplash

String

Tästä päästäänkin artikkelin seuraavaan osaan.

Voidaan nähdä, että objektin human name-ominaisuus on tyyppiä string. Jos sitten suoritamme dir(human.name) ominaisuudelle ja suodatamme pois metodit, joita ympäröi kaksinkertainen alleviivaus, huomaamme, että taikametodeja on yhteensä 33.

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

Selitän tässä neljä keskeistä metodia, koska artikkelin loppuosassa on jo tuotu esiin suurin osa taikametodeista.

__contains__

Tämä metodi suoritetaan, kun yritämme tarkistaa, onko tietty merkki olemassa.

__len__

Tämä metodi palauttaa merkkijonon pituuden. Se suoritetaan, kun suoritamme len()-metodin.

Jos haluamme laskea vain tietyt merkit merkkijonon pituuden laskemiseksi, metodi __()__ voidaan ohittaa tämän toiminnallisuuden tarjoamiseksi.

__repr__

Tämä metodi suoritetaan, kun haluamme luoda objektista kehittäjäystävällisen esityksen.

Huomaa, että __repr__ suoritetaan, kun print(object), jos meillä ei ole __str__()-toteutusta luokassamme.

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

Tämä tulostaa:

id=1 (<class ’int’>). name=Farhad (<luokka ’str’>)

Metodin __repr__() pitäisi olla tarkoitettu tuottamaan objektin virallinen esitys.

__iadd__

Joskus käytämme yhteenlaskuoperaattoria osoituksen e rinnalla.g.

self.id += 1

Tämä vastaa self.id = self.id + 1

Metodi iadd() suoritetaan, kun suoritamme yhteenlaskun osoituksen kanssa.

Lisäksi metodi __ipow__() suoritetaan, kun **= suoritetaan.

Taikamenetelmä __iand__() on bittien välisen JA:n suorittamiseen osoituksen kanssa ja __ior__() kutsutaan, kun yritämme tehdä != kuten: i != j

Lista

Tästä pääsemme artikkelin seuraavaan osaan. Objektin human osoiteominaisuus on tyypiltään list. Jos sitten suoritamme osoitteet-ominaisuudelle dir(human.addresses) ja suodatamme pois metodit, joita ympäröi kaksinkertainen alleviivaus, törmäämme siihen, että taikametodeja on yhteensä 35.

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

Esittelen tässä keskeisimmät metodit:

__reduce__

Kun objekti poimitaan, suoritetaan metodi __reduce__(), jonka tarkoituksena on palauttaa objekti, joka auttaa poimijaa ymmärtämään, miten se voidaan rakentaa takaisin.

__reduce_ex__

Pickle suosii __reduce_ex__()-metodia __reduce__()-metodin sijaan.

Metodi __reduce_ex__() ottaa vastaan kokonaislukuargumentin, joka on protokollan versio. Se tarjoaa taaksepäin yhteensopivuuden poiminnalle ja sitä käytetään poimitun tavuvirran rakentamiseen objektiin.

__reversed__

Metodi __reversed__() suoritetaan, kun yritämme kääntää kokoelmaa käänteisessä järjestyksessä.

Metodi suoritetaan, kun kutsutaan reversed(collection) tai collection.reverse(). Joskus päätämme muuttaa reversed()-metodin toiminnallisuutta.

Yllekytkemällä __reversed__()-metodin voimme saavuttaa halutun lopputuloksen.

Sanakirja

Tästä pääsemme artikkelin viidenteen osioon.

Sanakirja on yksi tärkeimmistä sisäänrakennetuista tyypeistä Pythonissa. Jos suoritamme dir(human.maps) ja suodatamme pois metodit, joita ympäröi kaksinkertainen alleviivaus, törmäämme siihen, että siellä on yhteensä 29 maagista metodia.

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

Tästä 29:stä maagisesta metodista selitän tässä neljä keskeisintä metodia:

__delitem__

Tämä metodi suoritetaan, kun poistamme elementin e.g.

del dictionary

__getitem__

Tämä metodi suoritetaan, kun yritämme saada kohteen johonkin avaimeen:

item = dictionary

__setitem__

Tämä metodi suoritetaan, kun yritämme asettaa elementin sanakirjassa:

dictionary = item

__iter__

Tämä metodi palauttaa kokoelman iteraattorin. Iteraattori auttaa meitä iteroimaan kokoelmaa.

Voitamme ohittaa iterator()-metodin suoritustavan ohittamalla __iter__()-metodin.

Viimeiseksi huomautus __call__()

Mitä jos haluaisimme tehdä objektistamme kutsuttavan? Ajatellaanpa, että haluaisimme tehdä human-oliosta kutsuttavan human()-funktion?

__call__()-metodin avulla voimme kohdella luokkia funktioina.

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

Saamme tämän toiminnallisuuden aikaan tarjoamalla Human-luokassamme maagisen __call__()-metodin toteutuksen.

Tämä tulostaa:

Yritit kutsua
Argumentteja: ()
Sana-argumentit: ()
Sana-argumentit: {}
id=1 (<luokka ’int’>). name=Farhad (<luokka ’str’>)
Kutsu suoritettu

Yllekytkemällä __call__()-metodin voimme nyt implementoida koristeen, jolla voimme palauttaa objektin funktioksi tai jopa kutsua niitä kirjastoja, jotka hyväksyvät funktioita argumentteina, välittämällä varsinaiset objektit.

Photo by Dollar Gill on Unsplash

Yhteenveto

Tämä on edistyneemmälle tasolle suunnattu aihe Python-kehittäjille ja suosittelen sitä kaikille, jotka käyttävät tai aikovat käyttää Python-ohjelmointikieltä.

Tässä artikkelissa on tarkoitus selittää, mitä maagiset metodit ovat ja miten niitä voidaan käyttää Python-sovellusten rakentamisessa. Se antoi yleiskatsauksen yleisimmin käytetyistä taikametodeista mukautetussa luokassa, kokonaisluvuista, merkkijonoista, listoista ja sanakirjatietotyypeistä.

Vaikka kaikki Pythonissa käytetyt metodit ovat julkisia, koodauskäytäntö on ympäröidä kaikki yksityiset metodit kaksinkertaisilla alleviivausmerkeillä __<metodi>__()

Tämä viittaa siihen, että taikametodit on tarkoitettu yksityisiksi metodeiksi. Se tarkoittaa myös sitä, että objektin kutsujan ei pitäisi suoraan kutsua metodia, vaan metodia kutsuu luokan sisäisesti se luokka, jolla on taikametodi. Taikametodien avulla voimme hallita enemmän sitä, miten sovelluksemme käyttäytyy.

Taikametodit voidaan ohittaa toiminnallisuuden rikastuttamiseksi ja mukautetun logiikan luomiseksi, joka vastaa parhaiten liiketoiminnan tarpeita.

Keskeisten taikametodien hahmottamisen tarkoituksena on, että ymmärrämme, haluammeko ohittaa nämä metodit omissa mukautetuissa luokissamme sovellusten rikastuttamiseksi.

Vastaa

Sähköpostiosoitettasi ei julkaista.