jaz v Pythonu, demistificiran

Če že nekaj časa programirate v Pythonu (objektno usmerjeno programiranje), ste zagotovo naleteli na metode, ki imajo selfza prvi parameter.

Najprej poskusimo razumeti, kaj je ta ponavljajoči se lastni parameter.

Kaj je self v Pythonu?

Pri objektno usmerjenem programiranju vedno, kadar definiramo metode za razred, uporabimo selfkot prvi parameter v vsakem primeru. Poglejmo si definicijo razreda z imenom 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")

V tem primeru imajo vse metode, vključno z __init__, prvi parameter kot self.

Vemo, da je razred načrt za predmete. Ta načrt se lahko uporablja za ustvarjanje več številk predmetov. Ustvarimo dva različna predmeta iz zgornjega razreda.

 cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)

Ključna selfbeseda se uporablja za predstavitev primerka (predmeta) danega razreda. V tem primeru sta oba Catpredmeta cat1in cat2svoje namein ageatribute. Če ni bilo lastnega argumenta, isti razred ni mogel hraniti informacij za oba predmeta.

Ker pa je razred le načrt, selfomogoča dostop do atributov in metod vsakega predmeta v pythonu. To omogoča vsakemu objektu lastne atribute in metode. Tako se že dolgo pred ustvarjanjem teh predmetov nanje sklicujemo kot selfmed definiranjem razreda.

Zakaj je self izrecno definiran vsakič?

Tudi ko razumemo uporabo self, se morda še vedno zdi čudno, zlasti programerjem iz drugih jezikov, ki selfse kot parameter izrecno posredujejo vsakič, ko določimo metodo. Kot piše Zen iz Pythona , je " eksplicitno boljše kot implicitno ".

Torej, zakaj moramo to storiti? Za začetek vzemimo preprost primer. Imamo Pointrazred, ki opredeljuje metodo distanceza izračun razdalje od začetka.

 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

Zdaj primerimo ta razred in poiščimo razdaljo.

 >>> p1 = Point(6,8) >>> p1.distance() 10.0

V zgornjem primeru __init__()določa tri parametre, vendar smo pravkar opravili dva (6 in 8). Podobno distance()zahteva en, vendar ni bilo posredovanih nič argumentov. Zakaj se Python ne pritožuje zaradi neujemanja tega argumenta?

Kaj se dogaja znotraj?

Point.distancein p1.distancev zgornjem primeru so različni in ne povsem enaki.

 >>> type(Point.distance) >>> type(p1.distance) 

Vidimo lahko, da je prva funkcija, druga pa metoda. Nenavadno pri metodah (v Pythonu) je, da se objekt sam preda kot prvi argument ustrezni funkciji.

V primeru zgornjega primera je klic metode p1.distance()dejansko enakovreden Point.distance(p1).

Na splošno, ko prikličemo metodo z nekaterimi argumenti, se prikliče ustrezna funkcija razreda, tako da se objekt postavi pred prvi argument. Torej, kaj podobnega obj.meth(args)postane Class.meth(obj, args). Klicni postopek je samodejen, postopek sprejemanja pa ne (izrecno).

To je razlog, da mora biti prvi parameter funkcije v razredu sam objekt. Zapis tega parametra selfje zgolj dogovor. To ni ključna beseda in v Pythonu nima posebnega pomena. Lahko bi uporabili druga imena (na primer this), vendar je zelo odsvetovano. Uporaba imen, ki selfse jih večina razvijalcev ne strinja, in poslabša berljivost kode ( berljivost šteje ).

Self se lahko izognili

Zdaj že jasno, da se sam objekt (primerek) samodejno posreduje kot prvi argument. Temu implicitnemu vedenju se je mogoče izogniti pri izdelavi statične metode. Upoštevajte naslednji preprost primer:

 class A(object): @staticmethod def stat_meth(): print("Look no self was passed")

Tukaj @staticmethodje dekorater funkcij, ki naredi stat_meth()statično. Ustvarimo primer tega razreda in pokličemo metodo.

 >>> a = A() >>> a.stat_meth() Look no self was passed

Iz zgornjega primera lahko vidimo, da se je implicitnemu vedenju posredovanja predmeta kot prvega argumenta med statično metodo izognili. Skratka, statične metode se obnašajo kot običajne stare funkcije (Ker imajo vsi predmeti razreda skupne statične metode).

 >>> type(A.stat_meth) >>> type(a.stat_meth) 

Self je tu, da ostane

Izrecno selfni edinstveno za Python. Ta ideja si je sposodila pri Modula-3 . Sledi primer uporabe, ko postane koristen.

V Pythonu ni izrecne izjave spremenljivke. Začnejo delovati že pri prvi nalogi. Uporaba selfenostavneje razlikuje med atributi primerka (in metodami) od lokalnih spremenljivk.

V prvem primeru je self.x atribut primerka, medtem ko je x lokalna spremenljivka. Niso enaki in ležijo v različnih imenskih prostorih.

Številni so predlagali, da si v Pythonu naredijo ključno besedo, kot sta thisC ++ in Java. To bi odpravilo odvečno uporabo eksplicitnega selfs seznama formalnih parametrov v metodah.

Čeprav se ta ideja zdi obetavna, se ne bo zgodila. Vsaj v bližnji prihodnosti ne. Glavni razlog je povratna združljivost. Tukaj je blog samega ustvarjalca Pythona, ki pojasnjuje, zakaj mora eksplicitni jaz ostati.

__init __ () ni konstruktor

Pomemben zaključek, ki ga lahko __init__()dobimo iz dosedanjih informacij, je, da metoda ni konstruktor. Številni naivni programerji Pythona se z njim zmedejo, saj jih __init__()pokličejo, ko ustvarimo objekt.

Natančnejši pregled bo pokazal, da je prvi parameter v __init__()sam predmet (objekt že obstaja). Funkcija __init__()se pokliče takoj po ustvarjanju predmeta in se uporablja za njegovo inicializacijo.

Tehnično gledano je konstruktor metoda, ki ustvari objekt sam. V Pythonu je ta metoda __new__(). Pogost podpis te metode je:

 __new__(cls, *args, **kwargs)

Ko __new__()je poklican, se razred sam preda kot prvi argument samodejno ( cls).

Kot tudi self, je tudi cls le poimenovanje. Poleg tega se * args in ** kwargs uporabijo za poljubno število argumentov med klici metod v Pythonu.

Nekaj ​​pomembnih stvari, ki si jih morate zapomniti pri izvajanju, __new__()so:

  • __new__()je vedno poklican prej __init__().
  • Prvi argument je razred sam, ki se posreduje implicitno.
  • Vedno vrni veljaven predmet iz __new__(). Ni obvezno, vendar je njegova glavna uporaba ustvarjanje in vrnitev predmeta.

Oglejmo si primer:

 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

Zdaj pa naredimo primer.

 >>> p2 = Point(3,4) From new (3, 4) () From init

Ta primer ponazarja, kar __new__()je bilo poklicano prej __init__(). Prav tako lahko vidimo, da je parameter cls in __new__()razred sam ( Point). Končno se objekt ustvari s klicanjem __new__()metode v osnovnem razredu predmeta .

V Pythonu objectje osnovni razred, iz katerega izhajajo vsi drugi razredi. V zgornjem primeru smo to storili s pomočjo super ().

Ali želite uporabiti __new__ ali __init__?

You might have seen __init__() very often but the use of __new__() is rare. This is because most of the time you don't need to override it. Generally, __init__() is used to initialize a newly created object while __new__() is used to control the way an object is created.

We can also use __new__() to initialize attributes of an object, but logically it should be inside __init__().

One practical use of __new__(), however, could be to restrict the number of objects created from a class.

Suppose we wanted a class SqPoint for creating instances to represent the four vertices of a square. We can inherit from our previous class Point (the second example in this article) and use __new__() to implement this restriction. Here is an example to restrict a class to have only four instances.

 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)

Vzorec:

 >>> 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

Zanimive Članki...