A Python egy objektumorientált nyelv, „alapvetően” ugyanolyan utasítások vannak benne, mint a többi oo programnyelben.
{
és }
helyett beljebb kezdés jelöli (és egy kettőspont a blokkot nyitó utasítás végén).def
vezeti be.def repeat(string):
if "kl" in string:
print('Jól van!')
return string*2
else:
return string*4
print(repeat("Négy "))
print(repeat("kl "))
Négy Négy Négy Négy Jól van! kl kl
Az osztályokat class
vezeti be, a metódusokat osztályon belül def
-fel kell definiálni.
Bizonyos speciális funkciókat (konstruktor, operátorok felülírása) olyan metódusok látnak el, amelyeknek a neve két aláhúzásjellel kezdődik és végződik:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '{} ({})'.format(self.name, self.age)
def do_something(self, thing):
print(self, thing+'!')
class Knight(Person):
def __str__(self):
return "Sir "+super().__str__()
robin = Knight("Robin", 42)
robin.do_something("bátran elszaladt")
Sir Robin (42) bátran elszaladt!
__init__
az osztály konstruktora; új objektum létrehozásához egyszerűen meghívjuk paraméterekkel az osztályt (mint a 14. sorban) és ennek hatására hívja meg a rendszer az __init__
metódust (ebben a példában a Knight(...)
hívás a Person
-tól örökölt __init__
-et hívja).
__str__
a sztring-konverzió metódus. Ezt automatikusan meghívja (többek között például) a beépített print
függvény, aminek sztringgé kell alakítani a paramétereit, mielőtt a képernyőre írhatná azokat.
Egy függvény definíciója (a def
utasítás) két dolgot csinál: létrehoz egy függvény objektumot és azt eltárolja olyan néven, amit megadtunk.
A dekorátorok lehetővé teszik, hogy valamit „beszúrjunk” eközé a két lépés közé: létrejön a függvény objektum, meghívódik a dekorátor és megkapja paraméterként az éppen létrejött függvény objektumot, majd a dekorátor visszatérési értéke eltárolódik olyan néven, amit a függvény definíciójánál megadtunk.
@callable_used_as_decorator
def new_function(arguments):
#... function body
Ahogyan fentebb leírtuk, ez nagyjából annak felel meg, mintha azt írtuk volna, hogy:
def _temporary_function_object(arguments):
#... function body
new_function = callable_used_as_decorator(_temporary_function_object)
(Leszámítva persze azt, hogy a dekorátornál nem tárolódik el ideiglenes változóban (_temporary_function_object
) a függvényünk.)
Példaképpen ez a (gyakorlatban nem túl hasznos) függvény meghívja a megkapott függvény objektumot, majd módosítás nélkül visszakapja azt:
def run_immediately(func):
func()
return func
Ha ezt dekorátorként használjuk, akkor az történik, amit ígértünk – a függvény azonnal lefut a definiálása után és később úgy használható, mintha mi sem történt volna:
@run_immediately
def greet():
print("Üdvözöllek, dicső lovag!")
print("Tram Tram tatam")
greet()
greet()
Üdvözöllek, dicső lovag! Tram Tram tatam Üdvözöllek, dicső lovag! Üdvözöllek, dicső lovag!
Általában azonban olyan dolgokat akarunk dekorátorként használni, amelyek valahogy módosítják az éppen definiált függvényt.
A függvény „módosítására” két módszer is van, először azt mutatjuk be, ami C++-ban is létezik (viszont Pythonban ritkábban használt).
Itt azt csináljuk, hogy valójában nem is függvényt adunk vissza, hanem egy olyan objektumot, ami függvényként használható, mert felülírta a függvényhívás operátort (van neki __call__
metódusa):
class cached:
def __init__(self, func):
self.func = func
self.cache = {} # üres hash-tábla
def __call__(self, arg):
try:
return self.cache[arg]
except KeyError:
result = self.cache[arg] = self.func(arg)
return result
@cached
def ask_for_value(name):
return input(name+" értéke? ")
print(ask_for_value)
results = []
results.append(ask_for_value("x"))
results.append(ask_for_value("y"))
results.append(ask_for_value("x"))
print(results)
<__main__.cached object at 0x0000014904918248> x értéke? 2 y értéke? 3 ['2', '3', '2']
Ahogyan láthatjuk, ask_for_value
néven valójában nem egy függvény, hanem egy cached
típusú objektum van eltárolva, ami azt csinálja, amit szeretnénk: bekéri x
értékét, bekéri y
értékét, majd nem kéri be újra x
értékét, mert azt már megmondtuk neki.
cached
implementáció csak egyparaméteres függvényeket kezel, de nem lett volna sokkal bonyolultabb olyat írni, ami akárhány paramétert kezel.__call__
metódus akkor hívódik meg, ha a megfelelő típusú objektumot függvényként kezeljük és paramétereket adunk át neki.A második megoldás azon alapul, hogy Pythonban „jól” lehet függvényen belül függvényeket definiálni: Ha a belső függvény használja a külső függvény egy lokális változóját, akkor egy closure jön létre és a belső függvény „elkapja és magával viszi” azokat a lokális változókat.
Ennek a bonyolult folyamatnak az az eredménye, hogy a következő kód ugyanúgy gyorsítótárazást valósít meg, mint az előző példa:
def cached(func):
cache = {}
def wrapper(arg):
try:
return cache[arg]
except KeyError:
result = cache[arg] = func(arg)
return result
return wrapper
@cached
def ask_for_value(name):
return input(name+" értéke? ")
print(ask_for_value)
results = []
results.append(ask_for_value("x"))
results.append(ask_for_value("y"))
results.append(ask_for_value("x"))
print(results)
<function cached.<locals>.wrapper at 0x00000149048F53A8> x értéke? 2 y értéke? 3 ['2', '3', '2']
Vegyük észre, hogy mostmár valóban egy függvény típusú dolog van eltárolva az ask_for_value
néven, azonban ennek a metaadatai (például neve) nem stimmelnek.
Ennek az esztétikai problémának a korrigálására lehet importálni a functools.wraps
függvényt, ami helyreteszi a metaadatokat:
import functools
def cached(func):
cache = {}
@functools.wraps(func)
def wrapper(arg):
try:
return cache[arg]
except KeyError:
result = cache[arg] = func(arg)
return result
return wrapper
@cached
def ask_for_value(name):
'''Így szokás dokumentációt írni Pythonban.'''
return input(name+" értéke? ")
print(ask_for_value)
print("Fontos metaadatok:")
print("Név:", ask_for_value.__name__)
print("Dokumentáció:", ask_for_value.__doc__)
#futtatás kihagyva, ugyanúgy működne, mint előbb
<function ask_for_value at 0x00000149048F54C8> Fontos metaadatok: Név: ask_for_value Dokumentáció: Így szokás dokumentációt írni Pythonban.
Persze kézzel is átállítgathattuk volna a metaadatokat (aki nagyon kíváncsi: itt a functools modul forráskódja), de ez így elegánsabb és rövidebb kód.
Ez a példa illusztrálja, hogy a dekorátor kijelölésekor lehet adattag-elérést (pont operátor) és függvényhívást alkalmazni. (Más operátorokat viszont nem, lásd a def
utasítás leírását a Python Language Reference-ben.)
Pontosabban fogalmazva functools.wraps
nem egy dekorátor, hanem egy dekorátor factory: paraméterül kap egy függvényt (ahonnan veszi a metaadatok értékeit) és a visszatérési értékét fogjuk dekorátorként használni.
Mi is tudunk ilyen dekorátor factory-t írni, bár ehhez kicsit sok egymásba ágyazott függvény fog kelleni. A példánk azt valósítja meg, hogy a dekorált függvény minden meghívása után az eredmény legyen naplózva (egy bizonyos fájlba, bizonyos üzenettel felcímkézve):
from functools import wraps
def logged(file, msg):
def decorator(func):
@wraps(func)
def wrapper(*args, **kw):
result = func(*args, **kw)
file.write(msg + str(result) + "\n")
return result
return wrapper
return decorator
import sys
@logged(sys.stderr, "Osztás eredménye: ") ## Rózsszinezés
def divide(x, y):
return x/y
[divide(2,2), divide(16,-8), divide(1,8)]
Osztás eredménye: 1.0 Osztás eredménye: -2.0 Osztás eredménye: 0.125
[1.0, -2.0, 0.125]
Itt sys.stderr a sztenderd hiba kimenet, amit békés rózsaszín háttérrel jelenít meg a Jupyter rendszer.
Ezt is megvalósíthatjuk closure-ök nélkül, C++ stílusban, de ehhez meglehetősen sokat kell írni:
class ResultLogger:
def __init__(self, func, file, msg):
self.func = func
self.file = file
self.msg = msg
def __call__(self, *args, **kw):
result = self.func(*args, **kw)
self.file.write(self.msg + str(result) + "\n")
return result
class logged:
def __init__(self, file, msg):
self.file = file
self.msg = msg
def __call__(self, func):
return ResultLogger(func, self.file, self.msg)
import sys
@logged(sys.stderr, "Összeadás eredménye: ")
def add(x, y):
return x+y
[add(2,2), add(6,-8), add(3,3)]
Összeadás eredménye: 4 Összeadás eredménye: -2 Összeadás eredménye: 6
[4, -2, 6]
Dekorátorokat nem csak függvényekre, hanem osztályokra is lehet alkalmazni. Például a rendezési operátorok definícióját megcsinálja nekünk a functools.total_ordering
dekorátor (csak az egyenlőséget és egy egyenlőtlenséget kell nekünk definiálnunk):
import functools
@functools.total_ordering
class Results:
def __init__(self, win, loss):
self.win = win
self.loss = loss
def adventage(self):
return self.win-self.loss
def __eq__(self, oth):
""" operator==() """
return self.win == oth.win and self.loss == oth.loss
def __lt__(self, oth):
""" operator<() """
return (self.adventage(), self.win) < (oth.adventage(), oth.win)
x = Results(6,3)
y = Results(4,2)
z = Results(4,1)
w = Results(3,0)
print(x>=y, x<=z, x!=w, w<x, x<x)
True False True True False
Egy property egy olyan dolog, ami egy közönséges adattagnak látszik, de valójában valamilyen függvényeket hív meg, amikor adatot írnak bele/adatot olvasnak ki belőle. Egy property legegyszerűbben dekorátorok segítségével hozható létre:
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@property
def angle(self):
return math.atan2(self.x, self.y)
@property
def r(self):
return math.sqrt(self.x**2 + self.y**2)
p = Point(3,4)
print(p.angle, p.r)
0.6435011087932844 5.0
Ezek most csak olvasható adattagként viselkednek:
p.r = 10
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-28-0c2e875cc88f> in <module> ----> 1 p.r = 10 AttributeError: can't set attribute
... de definiálhatóak hozzájuk setterek is:
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@property
def angle(self):
return math.atan2(self.x, self.y)
@angle.setter
def angle(self, value):
r = self.r
self.x = math.cos(value)*r
self.y = math.sin(value)*r
@property
def r(self):
return math.sqrt(self.x**2 + self.y**2)
@r.setter
def r(self, value):
angle = self.angle
self.x = math.cos(angle)*value
self.y = math.sin(angle)*value
p = Point(3,4)
p.r = 10
print(p.x, p.y)
8.0 6.0
Ahogyan látható, bármilyen számításokat elrejthetünk a property mögött, ennek persze az az ára, hogy a Python rendszer nem tudja és nem akarja ellenőrizni azt, hogy a property valóban kulturált adattagként viselkedik-e (például ha beleírunk egy értéket, akkor utána ugyanaz az érték lesz-e kiolvasható).
A propertyk létezésének nagy előnye, hogy nekik köszönhetően Pythonban egy osztály „publikus” interfészében nyugodtan lehetnek publikus adattagok (viszont nem illik get_foobar()
/ set_foobar()
jellegű gettereket és settereket írni).
Ha egy adattaghoz később extra funkcionalitást akarunk csatolni (például egy beállítás-fájlból akarjuk kiolvasni vagy ellenőrizni akarjuk, hogy csak megfelelő értéket lehessen beleírni stb.), akkor bármikor lecserélhetjük egy property-re. (Az adattagok többségénél viszont ez sohasem fog bekövetkezni és azoknál élvezhetjük, hogy nem hígítják fel getter-setter metódusok a kódunkat.)
A property
típus (aminek a konstruktorát az előbb dekorátorként használtuk) nem egyedülálló – hasonló működésre képes bármilyen más típus, ami megvalósítja a megfelelő dupla-aláhúzásjeles metódusokat. (Ugyanúgy, mint ahogy rendezési operátorok alkalmazhatóak minden objektumra, ami __le__
, __lt__
, stb. metódusokkal rendelkezik és függvényekhez hasonlóan meghívható minden objektum, ami __call__
metódust definiál.)
Figyeljük meg az előző példákban, hogy a property objektumok egy osztálynak az adattagjai voltak (C++ szóhasználattal static member
-ek) és olyankor viselkedtek furcsán, amikor az adattag-elérés operátor (pont operátor) segítségével „piszkáltuk” őket.
Összesen négy metódus felel ezért a viselkedésért, de ezek közül csak kettő igazán fontos: a __get__
és __set__
metódusok. (A másik kettő a __delete__
és a __set_name__
metódus, ezek leírása megtalálható dokumentációban.)
__get__
metódus¶Egy $P$ objektumnak a __get__
metódusa akkor kaphat szerepet, ha valamikor az adattag-elérés (pont operátor) a $P$ objektumot adná vissza eredményül. Ilyenkor meghívódik a $P$.__get__
metódus és a pont operátor ennek a metódushívásnak az eredményét fogja visszaadni (ha $P$-nek nem lett volna __get__
metódusa, akkor maga $P$ lett volna az eredmény).
Hasonlat a folyamat illusztrálására:
- Van raktároknak egy hosszú sora (az adattagok).
- Legtöbb raktárban ládák vannak (
__get__
metódus nélküli objektumok), de van néhány raktár, ahol ehelyett kigyúrt hordárok várakoznak (__get__
metódussal rendelkező objektumok).- Jön egy teherautó egy bizonyos raktárhoz a sorból (adattag-elérés operátor); a sofőr bekiabál, hogy „Hozzátok az árukat!” (megpróbálja meghívni a
__get__
metódust).- Ha hordárok voltak a raktárban, akkor azok kijönnek, összeszedik innen-onnan a cuccot és bepakolják a teherautóba, majd tovább várakoznak a raktárban (az eredmény az adattag
__get__
metódusának a visszatérési értéke).- Ellenkező esetben a sofőr kénytelen-kelletlen kiszáll és a raktárban lévő dolgokat bepakolja a teherautóba (az eredmény maga az adattag).
Fontos korlátozás: Ahogyan fentebb is említettük, csak az osztályok adattagjaként (C++ szóhasználattal nagyjából: static member) tárolt objektumoknak a __get__
metódusai kaphatnak szerepet; nem-osztály objektum adattagjaként (C++ szóhasználattal nagyjából: instance member) tárolt objektum __get__
metódusai nem kaphatnak szerepet.
A __get__
metódust mindig (egy+)kettő paraméterrel hívja meg a rendszer:
self
, a property-objektum)None
, amikor közvetlen osztálytól kérjük az adattagotA következő példa bemutatja, hogy milyen helyzetben mik ezek a paraméterek:
class Prop:
def __init__(self, content):
self.content = content
def __get__(self, inst, cls):
print("__get__ meghívva, paraméterek: inst =", inst, "cls =", cls)
return self.content
def __str__(self):
return "Prop object containing "+str(self.content)
class Base:
p1 = Prop("one")
class Deriv(Base):
p2 = Prop("two")
def __init__(self):
self.p3 = Prop("three")
#adattag-elérés osztályon keresztül:
print(Base.p1)
print(Deriv.p1)
print(Deriv.p2)
#adattag-elérés példányokon keresztül:
b = Base()
print(b.p1)
d = Deriv()
print(d.p1)
print(d.p2)
print(d.p3) #ez nem hívja meg a __get__ metódust, mert p3 nem egy osztálynak az adattagja
__get__ meghívva, paraméterek: inst = None cls = <class '__main__.Base'> one __get__ meghívva, paraméterek: inst = None cls = <class '__main__.Deriv'> one __get__ meghívva, paraméterek: inst = None cls = <class '__main__.Deriv'> two __get__ meghívva, paraméterek: inst = <__main__.Base object at 0x7f9628231d68> cls = <class '__main__.Base'> one __get__ meghívva, paraméterek: inst = <__main__.Deriv object at 0x7f9628231780> cls = <class '__main__.Deriv'> one __get__ meghívva, paraméterek: inst = <__main__.Deriv object at 0x7f9628231780> cls = <class '__main__.Deriv'> two Prop object containing three
__set__
metódus¶Ha egy __set__
metódussal rendelkező $P$ objektum egy $C$ osztály adattagjaként (C++ szóhasználattal nagyjából: static member) van tárolva, akkor az „ügyintézőként” őrzi azt az adattag-nevet és intézkedik, ha a $C$ osztály egy $I$ példányának olyan nevű adattagjához rendel értéket egy utasítás.
Az objektum.adattag = valami
értékadás normális esetben az objektum
saját adattagjai között tárolná el a valami
értéket (az adattag
név alatt), de ez előtt megnézi, hogy objektum
típusa tárol-e adattag
név alatt egy __set__
metódussal rendelkező „ügyintézőt”. Ha van ilyen ügyintéző objektum, akkor (a normális ügymenet helyett) meghívódik annak a __set__
metódusa, ami ott tárolja el a kapott értéket, ahol akarja.
A __set__
metódus (egy+)kettő paraméterrel hívódik meg:
self
, a property-objektum)A következő kód csinál egy property-típust, ami típustesztelő adattagokat valósít meg ezt a két speciális metódust és a __set_name__
metódust használva (ami akkor hívódik meg, amikor az objektum egy osztálynak a statikus adattagjává válik valamilyen név alatt):
class IntMember:
def __set_name__(self, cls, name):
self.name = name
@property
def stored_as(self):
return "_"+self.name
def __get__(self, inst, cls):
if inst is None:
return self
return getattr(inst, self.stored_as)
def __set__(self, inst, value):
if not isinstance(value, int):
raise TypeError('the value of {} must be an integer'.format(self.name))
setattr(inst, self.stored_as, value)
class Point:
x = IntMember()
y = IntMember()
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '({}, {})'.format(self.x, self.y)
p = Point(4,5)
print(p)
p.y = 8
print("p.y módosítása után:", p)
print("Az x koordináta valójában p._x-ben van tárolva, értéke = ", p._x)
p.x = "ez nem szám" # hibát okoz
(4, 5) p.y módosítása után: (4, 8) Az x koordináta valójában p._x-ben van tárolva, értéke = 4
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-15-5598d77e7add> in <module>() 28 print("p.y módosítása után:", p) 29 print("Az x koordináta valójában p._x-ben van tárolva, értéke = ", p._x) ---> 30 p.x = "ez nem szám" # hibát okoz <ipython-input-15-5598d77e7add> in __set__(self, inst, value) 11 def __set__(self, inst, value): 12 if not isinstance(value, int): ---> 13 raise TypeError('the value of {} must be an integer'.format(self.name)) 14 setattr(inst, self.stored_as, value) 15 TypeError: the value of x must be an integer
Ahogyan eddig is folyamatosan használtam, Python-ban az osztályoknak vannak metódusaik, amelyeket az osztály törzsén belül kell definiálni, ugyanazzal a def
kulcsszóval, ami a hagyományos függvényeket is definiálja.
Valójában az a helyzet, hogy a függvény típus rendelkezik __get__
metódussal és emiatt ha egy osztálynak van egy függvény típusú adattagja és azt egy példányon keresztül érjük el, akkor a __get__
metódus „beilleszti” a példányt a függvény első paraméterének.
Például:
class C:
def f(self):
return 42
print("Osztályból elérve:", type(C.f), C.f("akármi"))
# C.f egy függvény – bármit megadhatunk első paraméterként
print("Ugyanez máshogy:", type(C.f.__get__(None, C)), C.f.__get__(None, C)("akármi"))
c = C()
print("Példányból elérve:", type(c.f), c.f())
print("Ugyanez máshogy:", type(C.f.__get__(c, C)), C.f.__get__(c, C)())
def make_pair(x, y):
"Ez egy teljesen közönséges függvény..."
return (x, y)
C.make_pair = make_pair #... amit eltárolunk az osztály adattagjaként
print("... és meghívunk metódusként:", c.make_pair("spam spam spam"))
Osztályból elérve: <class 'function'> 42 Ugyanez máshogy: <class 'function'> 42 Példányból elérve: <class 'method'> 42 Ugyanez máshogy: <class 'method'> 42 ... és meghívunk metódusként: (<__main__.C object at 0x7f96281b22b0>, 'spam spam spam')
A függvényeknek ezt a viselkedését könnyen utánozhatnánk, ha akarnánk:
from functools import partial
class MyMakePair:
"""A függvény típust _alaposan_ utánzó saját típus.
A függvényhívás operátor mellett a __get__ viselkedését
is utánozza."""
def __call__(self, x, y):
return (x, y)
def __get__(self, inst, cls):
if inst:
return partial(self, inst)
# inst beszúrása első paraméterként
else:
return self
class SomeClass:
custom_method = MyMakePair()
obj = SomeClass()
print(type(obj.custom_method))
print(obj.custom_method("spam spam spam"))
<class 'functools.partial'> (<__main__.SomeClass object at 0x7f96281ae438>, 'spam spam spam')
Pythonban is vannak statikus metódusok, ezeket – micsoda meglepetés – egy dekorátor segítségével vezetjük be:
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@staticmethod
def polar(r, angle):
return Point(r*math.cos(angle), r*math.sin(angle))
def __str__(self):
return f'({self.x}, {self.y})'
print(Point.polar(3, math.pi))
p = Point(5,3)
print("Also callable from instances:", p.polar(2,math.pi/4))
print("The real object:", Point.__dict__["polar"])
print("Getting from the class:", Point.polar)
print("Getting from an instance:", p.polar)
(-3.0, 3.6739403974420594e-16) Also callable from instances: (1.4142135623730951, 1.414213562373095) The real object: <staticmethod object at 0x7f9628143358> Getting from the class: <function Point.polar at 0x7f96281ac0d0> Getting from an instance: <function Point.polar at 0x7f96281ac0d0>
A staticmethod
típus C-ben van definiálva (és azonnal elérhető, nem kell hozzá importálni semmit), de Python-ban is könnyen megírható lenne:
class my_staticmethod:
def __init__(self, func):
self.func = func
def __get__(self, inst, cls):
return self.func
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@my_staticmethod
def polar(r, angle):
return Point(r*math.cos(angle), r*math.sin(angle))
def __str__(self):
return f'({self.x}, {self.y})'
print(Point.polar(3, math.pi))
p = Point(5,3)
print("Also callable from instances:", p.polar(2,math.pi/4))
print("The real object:", Point.__dict__["polar"])
print("Getting from the class:", Point.polar)
print("Getting from an instance:", p.polar)
(-3.0, 3.6739403974420594e-16) Also callable from instances: (1.4142135623730951, 1.414213562373095) The real object: <__main__.my_staticmethod object at 0x7f96281aee80> Getting from the class: <function Point.polar at 0x7f96281a91e0> Getting from an instance: <function Point.polar at 0x7f96281a91e0>
Megjegyzés: az __str__
függvényben egy formázott sztringliterált lett alkalmazva. Ez egy frissen (Python 3.6-ban) bevezetett nyelvi elem, amivel tömören lehet leírni sztringek összebarkácsolását.
Python-ban van még egy metódusfajta, a classmethod
, ami az aktuális osztályt kapja meg paraméterként. Ennek a staticmethod
-hoz hasonló szerepe van, azonban „jól viselkedik” öröklődés során.
Egy olyan osztály, aminek csak statikus adattagjai és classmethod
-jai vannak, gyakorlatilag úgy viselkedik, mintha egy szingleton objektum lenne.
Az előző példaprogramban a staticmethod
-ot classmethod
-ra cserélve a következő lesz a program:
import math
class Point:
X = 42
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def polar(cls, r, angle):
print(cls.X)
return cls(r*math.cos(angle), r*math.sin(angle))
def __str__(self):
return f'({self.x}, {self.y})'
class BarePoint(Point):
X = 67
def __str__(self):
return f'{self.x} {self.y}'
print(Point.polar(3, math.pi))
print(BarePoint.polar(3, math.pi))
p = BarePoint(5,3)
print("Also callable from instances:", p.polar(2,math.pi/4))
print("The real object:", Point.__dict__["polar"])
print("Getting from the class:", Point.polar)
print("Getting from another class:", BarePoint.polar)
print("Getting from an instance:", p.polar)
42 (-3.0, 3.6739403974420594e-16) 67 -3.0 3.6739403974420594e-16 67 Also callable from instances: 1.4142135623730951 1.414213562373095 The real object: <classmethod object at 0x7f96281490f0> Getting from the class: <bound method Point.polar of <class '__main__.Point'>> Getting from another class: <bound method Point.polar of <class '__main__.BarePoint'>> Getting from an instance: <bound method Point.polar of <class '__main__.BarePoint'>>
# Az object osztály egy alosztályát képezzük
class Human(object):
# Osztály szintű mező: az osztály összes példányában azonos
species = "Ember"
# Ez a függvény meghívódik az osztály példányosításakor.
def __init__(self, name):
# A paramétert értékül adjuk a példány name attribútumának
self.name = name
# Inicializálunk egy mezőt
self.age = 0
# Példány metódus. Minden metódus első paramétere a "self", a példány maga
def say(self, msg):
return "{0} mondja : {1}".format(self.name, msg)
# Egy osztálymetódus az osztály összes példány közt meg van osztva.
# Hívásukkor az első paraméter mindig a hívó osztály (cls).
@classmethod
def get_species(cls):
return cls.species
# Egy statikus metódus osztály és példányreferencia nélkül hívódik
@staticmethod
def gurgulazo():
return "Ha ha haaaa*"
# Egy property jelölésű függvény olyan, mint egy getter.
# Használatával az age mező egy csak-olvasható attribútummá válik.
@property
def age(self):
return self._age
# Így lehet settert megadni egy mezőhöz
@age.setter
def age(self, age):
self._age = age
# Így lehet egy mező törlését engedélyezni
@age.deleter
def age(self):
del self._age
i = Human(name="Lajos")
print (i.say("Üdvözöllek") )
# Hívjuk az osztály metódusunkat
print(i.get_species())
# Változtassuk meg az osztály szintű attribútumot
Human.species = "Gondolkodó ember"
# Hívjuk az osztály metódusunkat
print(i.get_species())
# metodus meghívása
print(Human.gurgulazo())
##
Lajos mondja : Üdvözöllek Ember Gondolkodó ember Ha ha haaaa*