Ako već neko vrijeme programirate na Pythonu (objektno orijentirano programiranje), onda ste definitivno naišli na metode kojima je self
prvi parametar.
Pokušajmo prvo shvatiti što je to parametar koji se ponavlja.
Što je self u Pythonu?
U objektno orijentiranom programiranju, kad god definiramo metode za klasu, koristimo self
kao prvi parametar u svakom slučaju. Pogledajmo definiciju klase tzv 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")
U ovom slučaju sve metode, uključujući __init__
, imaju prvi parametar kao self
.
Znamo da je klasa nacrt predmeta. Ovaj se nacrt može koristiti za stvaranje višestrukog broja objekata. Stvorimo dva različita objekta iz gornje klase.
cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)
self
Ključna riječ se koristi da predstavlja primjer (objekt) iz te klase. U ovom slučaju, dva Cat
predmeta cat1
i cat2
imaju vlastitu name
i age
atribute. Ako nije bilo vlastitog argumenta, ista klasa nije mogla sadržavati informacije za oba ova objekta.
Međutim, budući da je klasa samo nacrt, self
omogućuje pristup atributima i metodama svakog objekta u pythonu. To omogućuje da svaki objekt ima svoje atribute i metode. Stoga, čak i puno prije stvaranja ovih objekata, referenciramo ih kao da self
definiramo klasu.
Zašto se self izričito definira svaki put?
Čak i kad razumijemo upotrebu self
, to se i dalje može činiti neobičnim, posebno programerima koji dolaze iz drugih jezika, koji self
se kao parametar izričito prenosi svaki put kad definiramo metodu. Kako kaže Zen iz Pythona , " eksplicitno je bolje nego implicitno ".
Pa, zašto to trebamo učiniti? Uzmimo za početak jednostavan primjer. Imamo Point
klasu koja definira metodu distance
za izračunavanje udaljenosti od ishodišta.
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
Idemo sada napraviti instancu ove klase i pronaći udaljenost.
>>> p1 = Point(6,8) >>> p1.distance() 10.0
U gornjem primjeru __init__()
definira tri parametra, ali upravo smo prošli dva (6 i 8). Slično distance()
zahtijeva jedan, ali nije proslijeđeno nula argumenata. Zašto se Python ne žali na nepodudaranje broja argumenata?
Što se događa iznutra?
Point.distance
a p1.distance
u gornjem primjeru su različiti i nisu potpuno isti.
>>> type(Point.distance) >>> type(p1.distance)
Možemo vidjeti da je prva funkcija, a druga metoda. Neobična stvar kod metoda (u Pythonu) je ta što se sam objekt kao prvi argument prenosi odgovarajućoj funkciji.
U slučaju gornjeg primjera, poziv metode p1.distance()
zapravo je ekvivalentan Point.distance(p1)
.
Općenito, kada metodu pozivamo s nekim argumentima, odgovarajuća funkcija klase poziva se stavljanjem objekta metode ispred prvog argumenta. Dakle, bilo što slično obj.meth(args)
postaje Class.meth(obj, args)
. Proces pozivanja je automatski, dok postupak primanja nije (izričito).
To je razlog što prvi parametar funkcije u klasi mora biti sam objekt. Pisanje ovog parametra self
samo je konvencija. To nije ključna riječ i nema posebno značenje u Pythonu. Mogli bismo koristiti i druga imena (poput this
), ali to se jako obeshrabruje. Upotreba imena koja se ne self
osvrću na većinu programera i pogoršava čitljivost koda ( čitljivost se računa ).
Sebe se može izbjeći
Do sada ste već jasni da se sam objekt (instanca) automatski predaje kao prvi argument. Ovo se implicitno ponašanje može izbjeći tijekom izrade statičke metode. Razmotrimo sljedeći jednostavan primjer:
class A(object): @staticmethod def stat_meth(): print("Look no self was passed")
Ovdje @staticmethod
je uređivač funkcija koji čini stat_meth()
statički. Intencirajmo ovu klasu i pozovimo metodu.
>>> a = A() >>> a.stat_meth() Look no self was passed
Iz gornjeg primjera možemo vidjeti da je izbjegnuto implicitno ponašanje prosljeđivanja objekta kao prvog argumenta korištenjem statičke metode. Sve u svemu, statičke metode ponašaju se poput običnih starih funkcija (budući da svi objekti klase dijele statičke metode).
>>> type(A.stat_meth) >>> type(a.stat_meth)
Self je tu da ostane
Eksplicitno self
nije jedinstveno za Python. Ova ideja posuđena je od Modula-3 . Slijedi slučaj upotrebe kada to postaje korisno.
U Pythonu ne postoji eksplicitna deklaracija varijable. Oni stupaju u akciju na prvom zadatku. Korištenje self
olakšava razlikovanje atributa instance (i metoda) od lokalnih varijabli.
U prvom primjeru self.x je atribut instance, dok je x lokalna varijabla. Nisu isti i leže u različitim imenskim prostorima.
Mnogi su predložili da sami naprave ključnu riječ u Pythonu, poput this
C ++ i Jave. To bi eliminiralo suvišnu upotrebu eksplicitnog self
sa formalnog popisa parametara u metodama.
Iako se ova ideja čini obećavajućom, to se neće dogoditi. Barem ne u bliskoj budućnosti. Glavni razlog je povratna kompatibilnost. Evo bloga samog tvorca Pythona koji objašnjava zašto eksplicitno ja mora ostati.
__init __ () nije konstruktor
Jedan važan zaključak koji se može izvući iz dosadašnjih informacija jest da __init__()
metoda nije konstruktor. Mnogi se naivni programeri na Pythonu zbune s tim otkako ih __init__()
se pozove kad kreiramo objekt.
Pažljiviji pregled otkrit će da je prvi parametar u __init__()
sam objekt (objekt već postoji). Funkcija __init__()
se poziva odmah nakon stvaranja objekta i koristi se za njegovu inicijalizaciju.
Tehnički gledano, konstruktor je metoda koja stvara sam objekt. U Pythonu je ova metoda __new__()
. Uobičajeni potpis ove metode je:
__new__(cls, *args, **kwargs)
Kada __new__()
se pozove, sama klasa se automatski predaje kao prvi argument ( cls
).
Opet, poput self-a, cls je samo konvencija imenovanja. Nadalje, * args i ** kwargs koriste se za uzimanje proizvoljnog broja argumenata tijekom poziva metode u Pythonu.
Neke važne stvari koje morate zapamtiti prilikom implementacije __new__()
su:
__new__()
uvijek se zove prije__init__()
.- Prvi argument je sama klasa koja se implicitno prenosi.
- Uvijek vratite valjani objekt iz
__new__()
. Nije obvezno, ali njegova je glavna upotreba stvaranje i vraćanje objekta.
Pogledajmo primjer:
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
Sada, hajde da sada napravimo instancu.
>>> p2 = Point(3,4) From new (3, 4) () From init
Ovaj primjer ilustrira ono što __new__()
se naziva prije __init__()
. Također možemo vidjeti da je parametar cls in __new__()
sama klasa ( Point
). Konačno, objekt se kreira pozivanjem __new__()
metode na osnovnoj klasi objekta .
U Pythonu object
je osnovna klasa iz koje su izvedene sve ostale klase. U gornjem primjeru to smo učinili koristeći super ().
Koristiti __new__ ili __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)
Uzorak izvođenja:
>>> 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