Ha egy ideje Pythonban (objektum-orientált programozás) programoz, akkor biztosan találkozott olyan módszerekkel, amelyek self
első paramétere.
Először próbáljuk megérteni, mi ez a visszatérő énparaméter.
Mi az én a Pythonban?
Az objektumorientált programozás során, amikor egy osztályra meghatározunk módszereket self
, minden esetben első paraméterként használjuk . Nézzük meg az úgynevezett osztály definícióját 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")
Ebben az esetben az összes metódusnak, beleértve __init__
az első paramétert is self
.
Tudjuk, hogy az osztály az objektumok tervrajza. Ez a terv több objektumszám létrehozására használható. Hozzunk létre két különböző objektumot a fenti osztályból.
cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)
A self
kulcsszó az adott osztály egy példányának (objektumának) reprezentálására szolgál. Ebben az esetben a két Cat
tárgy cat1
és cat2
a saját name
és age
attribútumokat. Ha nem volt önérv, ugyanaz az osztály nem tudta megtartani mindkét objektum információit.
Mivel azonban az osztály csak tervrajz, self
hozzáférést biztosít a python egyes objektumainak attribútumaihoz és metódusaihoz. Ez lehetővé teszi, hogy minden objektum saját attribútumokkal és módszerekkel rendelkezzen. Így még jóval ezen objektumok létrehozása előtt is self
az osztály definiálásakor hivatkozunk az objektumokra .
Miért van mindig meghatározva az önmaga?
Még akkor is, ha megértjük a használatát self
, különösnek tűnhet, különösen a más nyelvből érkező programozók számára, amelyet self
minden egyes módszer megadásakor kifejezetten paraméterként adunk át. A The Python zenéje szerint "az explicit jobb, mint az implicit ".
Szóval, miért kell ezt tennünk? Vegyünk egy egyszerű példát az elején. Van egy Point
osztályunk, amely meghatározza az eredettől distance
való távolság kiszámításának módszerét .
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
Pillantsuk be most ezt az osztályt, és keressük meg a távolságot.
>>> p1 = Point(6,8) >>> p1.distance() 10.0
A fenti példában __init__()
három paramétert határoz meg, de kettőt (6 és 8) túlléptünk. Ehhez hasonlóan distance()
egy szükséges, de nulla argumentumot nem adtak meg. Miért nem panaszkodik a Python erre az érvszám-egyezésre?
Mi történik belsőleg?
Point.distance
és p1.distance
a fenti példában különböznek egymástól és nem pontosan ugyanazok.
>>> type(Point.distance) >>> type(p1.distance)
Láthatjuk, hogy az első egy függvény, a második pedig egy módszer. A metódusok sajátossága (Pythonban) az, hogy magát az objektumot adja át első argumentumként a megfelelő függvénynek.
A fenti példa esetében a metódushívás p1.distance()
valójában ekvivalens Point.distance(p1)
.
Általában, amikor metódust hívunk néhány argumentummal, a megfelelő osztályfüggvény meghívásra kerül, ha a metódus objektumát az első argumentum elé helyezzük. Szóval, bármi hasonlóvá obj.meth(args)
válik Class.meth(obj, args)
. A hívási folyamat automatikus, míg a fogadó folyamat nem (kifejezett).
Ez az oka annak, hogy az osztályban egy függvény első paraméterének magának az objektumnak kell lennie. Ennek a paraméternek az írása self
pusztán konvenció. Ez nem kulcsszó, és nincs különösebb jelentése a Pythonban. Használhatnánk más neveket is (például this
), de ez nagyon nem ajánlott. A self
legtöbb fejlesztő nem veszi figyelembe a nevek használatát, és rontja a kód olvashatóságát (az olvashatóság számít ).
Önmagát el lehet kerülni
Mostanra tisztában van azzal, hogy maga az objektum (példány) automatikusan átkerül első argumentumként. Ez az implicit viselkedés elkerülhető statikus módszer készítése közben . Tekintsük a következő egyszerű példát:
class A(object): @staticmethod def stat_meth(): print("Look no self was passed")
Itt @staticmethod
van egy funkció dekoratőr, amely stat_meth()
statikussá teszi . Példázzuk le ezt az osztályt és hívjuk meg a metódust.
>>> a = A() >>> a.stat_meth() Look no self was passed
A fenti példából láthatjuk, hogy az objektum első argumentumként való átadásának implicit magatartását statikus módszer használata során elkerülte. Összességében a statikus módszerek úgy viselkednek, mint a sima régi függvények (Mivel egy osztály összes objektuma osztozik statikus módszerekben).
>>> type(A.stat_meth) >>> type(a.stat_meth)
Self itt marad
Az explicit self
nem csak a Pythonra vonatkozik. Ezt az ötletet a Modula-3- tól kölcsönözték . Az alábbiakban olyan felhasználási esetet találunk, ahol hasznos lesz.
A Pythonban nincs kifejezett változó deklaráció. Az első megbízáskor cselekednek. A használata self
megkönnyíti a példányattribútumok (és módszerek) megkülönböztetését a helyi változóktól.
Az első példában az self.x egy példány attribútum, míg az x egy helyi változó. Nem azonosak és különböző névterekben fekszenek.
Sokan azt javasolták, hogy tegyék kulcsszóvá a Pythonban, például this
a C ++ és a Java programokban. Ez kiküszöbölné az explicit felesleges használatát self
a formális paraméterlistából a módszerekben.
Bár ez az ötlet ígéretesnek tűnik, nem fog megvalósulni. Legalábbis a közeljövőben nem. A fő ok a visszamenőleges kompatibilitás. Itt van egy blog a Python alkotójától, amely elmagyarázza, miért kell maradnia a kifejezett énnek.
A __init __ () nem konstruktor
Az eddigi információkból levonható egyik fontos következtetés az, hogy a __init__()
módszer nem konstruktor. Sok naiv Python programozó összezavarodik vele, mivel __init__()
egy objektum létrehozásakor hívják őket.
Közelebb vizsgálva kiderül, hogy az első paraméter __init__()
maga az objektum (az objektum már létezik). A függvény az objektum létrehozása után__init__()
azonnal meghívásra kerül, és inicializálásra szolgál.
Műszakilag a konstruktor olyan módszer, amely magát az objektumot hozza létre. A Pythonban ez a módszer az __new__()
. Ennek a módszernek a közös aláírása:
__new__(cls, *args, **kwargs)
Amikor __new__()
meghívjuk, magát az osztályt adjuk át automatikusan első argumentumként ( cls
).
Ismét, mint én, a cls is csak egy elnevezési szokás. Ezenkívül az * args és a ** kwargs segítségével tetszőlegesen sok argumentumot veszünk fel a Python metódushívások során.
Néhány fontos dolog, amelyet érdemes megjegyezni a megvalósítás __new__()
során:
__new__()
mindig hívják korábban__init__()
.- Az első érv maga az osztály, amelyet implicit módon adnak át.
- Mindig küldjön érvényes objektumot innen
__new__()
. Nem kötelező, de fő célja egy objektum létrehozása és visszaadása.
Nézzünk meg egy példát:
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
Most pedig példázzuk.
>>> p2 = Point(3,4) From new (3, 4) () From init
Ez a példa szemlélteti, hogy __new__()
korábban hívták __init__()
. Azt is láthatjuk, hogy az cls paraméter __new__()
maga az osztály ( Point
). Végül az objektum a __new__()
metódus objektum alaposztályon történő meghívásával jön létre .
A Pythonban object
az az alaposztály, amelyből az összes többi osztály származik. A fenti példában ezt a super () segítségével tettük meg.
Használja a __új__ vagy __init__ szót?
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)
Mintafuttatás:
>>> 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