Írunk egy Person osztályt. Minden embernek lesz neve és titulusa, a __str__
függvénnyel pedig megszólítjuk. A Knight osztály a Person leszármazottja. Minden lovag egyben ember is, de a megszólításuk egyedi. A leszármazott örökli az ősosztály összes tagváltozóját és metódusát.
class Person:
def __init__(self, name, title):
self.name = name
self.title = title
def __str__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name):
super().__init__(name, 'Sir')
varga = Person('Varga', 'Mr')
launcelot = Knight('Launcelot')
print(varga)
print(launcelot)
A leszármazott osztályban definiálhatunk új tagváltozókat is. Például legyen minden lovagnak egy opcionális epitheton ornansa!
class Person:
def __init__(self, name, title):
self.name = name
self.title = title
def __str__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name, eo=""):
super().__init__(name, 'Sir')
self.eo = eo
launcelot = Knight('Launcelot', 'the brave')
print(launcelot)
Minden lovag megérdemli, hogy a neve mellé az állandó jezőjét is hozzáfűzzük. Ehhez felüldefiniáljuk a __str__
metódust. Azonos nevű metódusok esetén mindig a leszármazottban definiált élvez elsőbbséget és hívódik meg.
class Person:
def __init__(self, name, title):
self.name = name
self.title = title
def __str__(self):
return self.title + " " + self.name
class Knight(Person):
def __init__(self, name, eo=""):
super().__init__(name, 'Sir')
self.eo = eo
def __str__(self):
if len(self.eo) > 0:
return self.title + " " + self.name + ", " + self.eo
else:
return super().__str__()
launcelot = Knight('Launcelot', 'the brave')
black = Knight('Black')
robin = Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')
print(launcelot)
print(black)
print(robin)
Van mód arra is, hogy bizonyos változókat osztályszinten definiáljunk. Ilyen például a lovagok "Sir" megszólítása.
Hogy ne essen egybe a Person title változójával, nevezzük special_title
-nak.
Ahhoz, hogy az osztályszintű változót elérjük, a Knight.special_title
-ra kell hivatkozni, de bármelyik példány .special_title
változója is erre mutat.
class Person:
def __init__(self, name, title):
self.name = name
self.title = title
def __str__(self):
return self.title + " " + self.name
class Knight(Person):
special_title = 'Sir'
def __init__(self, name, eo=""):
super().__init__(name, "")
self.eo = eo
def __str__(self):
return Knight.special_title + " " + self.name + ", " + self.eo
launcelot = Knight('Launcelot', 'the brave')
robin = Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')
print(launcelot)
print(robin)
print(robin.special_title, launcelot.special_title, Knight.special_title)
Megjegyezzük, hogy az öröklési lánc tetszőlegesen hosszú lehet, illetve lehet egyszerre több osztályból örökölni.
Vannak olyan esetek, amikor a kód hibába ütközik. Ilyenkor egy Exception típusú objektumot dob (emel) a Python.
Ekkor
Kivéve, ha
try:
...
except ... :
...
Egy Exception-t bármikor el lehet kapni, akármennyi függvényhívást túlél. Nézzünk egy példát! Most mi magunk hívjuk elő a hibát (raise), de egyben le is kezeljük.
try:
raise Exception('spam', 'eggs')
except Exception as inst:
print(type(inst))
print(inst.args)
print(inst)
x, y = inst.args
print('x =', x)
print('y =', y)
Most pedig lekezelünk egy nem mesterségesen létrehozott kivételt.
try:
str(5) + 5
except TypeError as inst:
print(type(inst))
print(inst.args)
print(inst)
Lássunk egy természetes példát! Olvassunk be egy valós számot és írjuk ki a kétszeresét! Ha a felhasználó nem valós számot ad, a float()
nem tudja azzá konvertálni, így hibát jelez, mi viszont ezt lekezeljük.
while True:
try:
x = float(input("Adjon meg egy valós számot: "))
break
except ValueError:
print("Hoppá! Ez nem valós szám, próbálja újra!")
print(2*x)
Nézzük meg hogy hol történt a kivétel dobása (raise) és hol az elkapása.
def f(x):
return x + 5 # nem jó ha x egy string
def g(x):
return x + x # ez jó int-re és str-re is
x = "5"
y = g(x)
z = f(y)
def beolvas(n):
maximum = float("-inf")
for i in range(n):
x = float(input())
if x > maximum:
maximum = x
return maximum
try:
y = beolvas(3)
print(y)
except ValueError as e:
print(e)
Írhatunk saját kivétel osztályt is, ezt arra tudjuk használni, hogy egyedi hibákat jelezzünk vele.
Csupán az Exception
beépített osztályból kell öröklődni (opcionálisan mást is lehet bele rakni).
class KnightException(Exception):
pass
try:
lancelot = Person("Lancelot", "Mr")
x = str(lancelot)
if x[:3] != "Sir":
raise KnightException("Use correct title")
except KnightException as s:
print(s)
Láttuk, hogy a for i in L
nem csak akkor működik, ha L
lista.
Pontosan mely objektumok állhatnak a for ... in
után?
A for
meghívja az iter()
függvényt, mellyel hozzájut az iterátorhoz (bejáró). Ebben definiálva van egy __next__()
metódus, amely minden meghívására visszaadja az objektum egy elemét. Ha a __next__()
nem talál több elemet, egy StopIteration
kivételt dob.
Iterátor az az objektum, aminek van __next__()
metódusa.
Minden iterátor objektum egyúttal iterálható objektum. Fordítva ez nem áll. A lista iterálható, de nem iterátor.
for ... in
után iterálható objektum állhat, aminek van __iter__()
metódusa, ami iterátor objektumot ad vissza. Ezek meghívhatók függvényként: x.__iter__()
ekvivalens az iter(x)
, x.__next__()
a next(x)
hívással.
Ezek speciális metódusok!
r = range(3) # iterálható (bejárható)
print(type(r))
it = iter(r) # iterátor (bejáró)
print(type(it))
next(it)
print(next(it))
print(next(it))
next(it)
Tetszőleges, általunk definiált osztályhoz is adható iterátor tulajdonság. Először is szükség van egy __iter__
függvényre, ami egy olyan objektummal tér vissza, ami iterátor, ez jelzi az iterálás kezdetét. Az iterátor objektumok definiálnak egy __next__()
metódust, ami sorban visszaadja az elemeket.
Ha kifogyott, akkor StopIteration kivételt emel.
class Person:
def __init__(self, name, title=""):
self.name = name
self.title = title
def __str__(self):
return self.title + " " + self.name
class Knight(Person):
title = 'Sir'
searching_for = 'The Holy Grail'
def __init__(self, name, eo):
super().__init__(name)
self.eo = eo
def __str__(self):
return Knight.title + " " + self.name + ", " + self.eo
class Group:
def __init__(self, name, persons):
self.persons = persons
self.name = name
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= len(self.persons):
raise StopIteration # dobunk egy kivételt
self.index += 1
return self.persons[self.index - 1]
kotrt = Group('Knights of The Round Table',
[Knight('Launcelot', 'the brave'),
Knight('Galahad', 'the pure'),
Knight('Bedevere', 'the wise'),
Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')])
for knight in kotrt:
print(knight)
Mindezt megoldhatjuk egyszerűbben a listát mint bejárható objektumot használva:
class Group:
def __init__(self, name, persons):
self.persons = persons
self.name = name
def __iter__(self): # A Group csoportot iterálhatóvá tesszük
return iter(self.persons)
kotrt = Group('Knights of The Round Table',
[Knight('Launcelot', 'the brave'),
Knight('Galahad', 'the pure'),
Knight('Bedevere', 'the wise'),
Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')])
for knight in kotrt: # a ciklus lefut egy Group objektumra
print(knight)
Ha a Group objektumot nem tesszük bejárhatóvá, akkor még mindig kiírhatjuk egy csoport összes személyét, hisz a .persons
tagváltozó lista (de ez már nem olyan elegáns):
class Group:
def __init__(self, name, persons):
self.persons = persons
self.name = name
kotrt = Group('Knights of The Round Table',
[Knight('Launcelot', 'the brave'),
Knight('Galahad', 'the pure'),
Knight('Bedevere', 'the wise'),
Knight('Robin', 'the Not-quite-so-brave-as-Sir-Launcelot')])
for knight in kotrt.persons: # kotrt nem, de kotrt.persons bejárható
print(knight)