Jos olet ohjelmoinut Pythonissa (olio-ohjelmointi) jonkin aikaa, olet ehdottomasti törmännyt menetelmiin, self
joiden ensimmäinen parametri on.
Yritetään ensin ymmärtää, mikä tämä toistuva itseparametri on.
Mikä on itse Pythonissa?
Kun määritämme olio-ohjelmoinnissa luokalle menetelmiä, käytämme self
kussakin tapauksessa ensimmäisenä parametrina. Katsotaanpa nimeltään luokan määritelmää Cat
.
class Cat: def __init__(self, name, age): self.name = name self.age = age def info(self): print(f"I am a cat. My name is (self.name). I am (self.age) years old.") def make_sound(self): print("Meow")
Tässä tapauksessa kaikkien menetelmien, mukaan lukien __init__
, ensimmäinen parametri on self
.
Tiedämme, että luokka on esineiden suunnitelma. Tätä suunnitelmaa voidaan käyttää luomaan useita objekteja. Luodaan kaksi erilaista objektia yllä olevasta luokasta.
cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)
self
Avainsanaa käytetään edustamaan esimerkiksi (kohde) annettua luokkaa. Tässä tapauksessa kaksi Cat
esineitä cat1
ja cat2
on omat name
ja age
ominaisuuksia. Jos itseargumenttia ei ollut, sama luokka ei voinut säilyttää molempien objektien tietoja.
Koska luokka on kuitenkin vain suunnitelma, self
sallii pääsyn pythonin kunkin objektin määritteisiin ja menetelmiin. Näin jokaisella objektilla voi olla omat määritteensä ja menetelmänsä. Siten, jopa kauan ennen näiden objektien luomista, viittaamme kohteisiin kuten self
luokkaa määriteltäessä.
Miksi itse määritellään nimenomaan joka kerta?
Vaikka ymmärrämme sen käytön self
, se voi silti tuntua oudolta, etenkin muilta kieliltä tuleville ohjelmoijille, joka self
välitetään parametrina nimenomaisesti joka kerta, kun määritämme menetelmän. Kuten The Zen of Python kertoo, " eksplisiittinen on parempi kuin implisiittinen ".
Joten, miksi meidän on tehtävä tämä? Otetaan aluksi yksinkertainen esimerkki. Meillä on Point
luokka, joka määrittelee menetelmän distance
etäisyyden laskemiseksi alkuperästä.
class Point(object): def __init__(self,x = 0,y = 0): self.x = x self.y = y def distance(self): """Find distance from origin""" return (self.x**2 + self.y**2) ** 0.5
Tehkäämme nyt tämä luokka ja etsimme etäisyys.
>>> p1 = Point(6,8) >>> p1.distance() 10.0
Yllä olevassa esimerkissä __init__()
määritetään kolme parametria, mutta ohitimme juuri kaksi (6 ja 8). Samoin distance()
vaatii yhden, mutta nolla argumenttia välitettiin. Miksi Python ei valittaa tästä argumenttinumeron ristiriitaisuudesta?
Mitä tapahtuu sisäisesti?
Point.distance
ja p1.distance
yllä olevassa esimerkissä ovat erilaiset eivätkä täysin samat.
>>> type(Point.distance) >>> type(p1.distance)
Voimme nähdä, että ensimmäinen on funktio ja toinen menetelmä. Menetelmien (Pythonissa) erikoinen asia on, että itse objekti välitetään ensimmäisenä argumenttina vastaavalle funktiolle.
Yllä olevan esimerkin tapauksessa menetelmän kutsu p1.distance()
vastaa itse asiassa Point.distance(p1)
.
Yleensä kun kutsumme menetelmää joidenkin argumenttien kanssa, vastaava luokkafunktio kutsutaan sijoittamalla metodin objekti ensimmäisen argumentin eteen. Joten kaikenlainen obj.meth(args)
tulee Class.meth(obj, args)
. Soittoprosessi on automaattinen, kun taas vastaanottoprosessi ei ole (sen nimenomainen).
Tästä syystä luokan funktion ensimmäisen parametrin on oltava objekti itse. Tämän parametrin kirjoittaminen self
on vain käytäntö. Se ei ole avainsana eikä sillä ole erityistä merkitystä Pythonissa. Voisimme käyttää muita nimiä (kuten this
), mutta sitä ei suositella. self
Useimmat kehittäjät paheksuvat muiden kuin nimien käyttöä ja heikentävät koodin luettavuutta ( Luettavuus laskee ).
Itse voidaan välttää
Tähän mennessä olet selvä, että itse objekti (instanssi) välitetään automaattisesti ensimmäisenä argumenttina. Tämä implisiittinen käyttäytyminen voidaan välttää staattista menetelmää tehtäessä . Harkitse seuraavaa yksinkertaista esimerkkiä:
class A(object): @staticmethod def stat_meth(): print("Look no self was passed")
Tässä @staticmethod
on toiminnon sisustaja, joka tekee stat_meth()
staattiseksi. Tehkäämme tämä luokka ja kutsumme menetelmä.
>>> a = A() >>> a.stat_meth() Look no self was passed
Yllä olevasta esimerkistä voimme nähdä, että kohteen välittäminen implisiittiseksi käytökseksi ensimmäisenä argumenttina vältettiin staattista menetelmää käytettäessä. Kaiken kaikkiaan staattiset menetelmät käyttäytyvät kuin tavalliset vanhat toiminnot (Koska kaikki luokan objektit jakavat staattisia menetelmiä).
>>> type(A.stat_meth) >>> type(a.stat_meth)
Itse on täällä jäädäkseen
Explicit self
ei ole ainutlaatuinen Pythonille. Tämä idea on lainattu Modula-3: lta . Seuraava on käyttötapaus, jossa siitä on hyötyä.
Pythonissa ei ole nimenomaista muuttujailmoitusta. Ne alkavat toimia ensimmäisessä tehtävässä. Käytön self
avulla on helpompaa erottaa ilmentymämääritteet (ja menetelmät) paikallisista muuttujista.
Ensimmäisessä esimerkissä self.x on ilmentymäattribuutti, kun taas x on paikallinen muuttuja. Ne eivät ole samoja ja ne sijaitsevat eri nimitiloissa.
Monet ovat ehdottaneet itsensä tekemistä avainsanaksi Pythonissa, kuten this
C ++ ja Java. Tämä eliminoisi self
muodollisen parametriluettelon nimenomaisen käytön menetelmissä.
Vaikka tämä idea näyttää lupaavalta, sitä ei tule tapahtumaan. Ainakin ei lähitulevaisuudessa. Tärkein syy on taaksepäin yhteensopivuus. Tässä on Pythonin luojan blogi, jossa selitetään, miksi nimenomaisen itsen on pysyttävä.
__init __ () ei ole rakentaja
Yksi tärkeä johtopäätös, joka voidaan tehdä tähänastisesta tiedosta, on se, että __init__()
menetelmä ei ole konstruktori. Monet naiivit Python-ohjelmoijat sekoittuvat siihen, koska __init__()
heidät kutsutaan, kun luomme objektin.
Tarkempi tarkastelu paljastaa, että ensimmäinen parametri __init__()
on itse objekti (objekti on jo olemassa). Toimintoa __init__()
kutsutaan välittömästi sen jälkeen, kun objekti on luotu, ja sitä käytetään sen alustamiseen.
Teknisesti konstruktori on menetelmä, joka luo itse objektin. Pythonissa tämä menetelmä on __new__()
. Tämän menetelmän yleinen allekirjoitus on:
__new__(cls, *args, **kwargs)
Kun __new__()
kutsutaan, luokka itse välitetään ensimmäisenä argumenttina automaattisesti ( cls
).
Jälleen, kuten itse, cls on vain nimeämiskäytäntö. Lisäksi * args ja ** kwargs käytetään mielivaltaisen määrän argumentteja metodikutsujen aikana Pythonissa.
Joitakin tärkeitä asioita, jotka on muistettava toteutuksessa __new__()
:
__new__()
soitetaan aina aiemmin__init__()
.- Ensimmäinen argumentti on luokka itse, joka hyväksytään implisiittisesti.
- Palauta aina kelvollinen esine kohteesta
__new__()
. Ei pakollinen, mutta sen pääasiallinen käyttötarkoitus on luoda ja palauttaa objekti.
Katsotaanpa esimerkkiä:
class Point(object): def __new__(cls,*args,**kwargs): print("From new") print(cls) print(args) print(kwargs) # create our object and return it obj = super().__new__(cls) return obj def __init__(self,x = 0,y = 0): print("From init") self.x = x self.y = y
Tehdään nyt se heti.
>>> p2 = Point(3,4) From new (3, 4) () From init
Tämä esimerkki kuvaa sitä, mitä __new__()
kutsutaan aikaisemmin __init__()
. Voimme myös nähdä, että parametri cls __new__()
on luokka itse ( Point
). Lopuksi objekti luodaan kutsumalla __new__()
menetelmä objektin perusluokkaan.
Pythonissa object
on perusluokka, josta kaikki muut luokat on johdettu. Yllä olevassa esimerkissä olemme tehneet tämän super (): lla.
Käytä __uusi__ tai __init__?
Olet ehkä nähnyt __init__()
hyvin usein, mutta niiden käyttö __new__()
on harvinaista. Tämä johtuu siitä, että suurimman osan ajasta sinun ei tarvitse korvata sitä. Yleensä __init__()
sitä käytetään uuden luodun objektin alustamiseen, kun taas __new__()
sitä käytetään objektin luomisen hallintaan.
Voimme käyttää myös __new__()
kohteen attribuuttien alustamiseen, mutta loogisesti sen pitäisi olla sisällä __init__()
.
Yksi käytännön käyttö __new__()
voisi kuitenkin olla luokasta luotujen objektien määrän rajoittaminen.
Oletetaan, että halusimme luokan SqPoint
esiintymien luomiseksi, jotka edustavat neliön neljää kärkeä . Voimme periä edellisestä luokastamme Point
(tämän artikkelin toinen esimerkki) ja käyttää __new__()
tämän rajoituksen toteuttamiseen. Tässä on esimerkki luokan rajoittamisesta siten, että siinä on vain neljä esiintymää.
class SqPoint(Point): MAX_Inst = 4 Inst_created = 0 def __new__(cls,*args,**kwargs): if (cls.Inst_created>= cls.MAX_Inst): raise ValueError("Cannot create more objects") cls.Inst_created += 1 return super().__new__(cls)
Näyte ajo:
>>> p1 = SqPoint(0,0) >>> p2 = SqPoint(1,0) >>> p3 = SqPoint(1,1) >>> p4 = SqPoint(0,1) >>> >>> p5 = SqPoint(2,2) Traceback (most recent call last):… ValueError: Cannot create more objects