Kategória: Python gyerekeknek.
Elmélet
Osztály
A szorosan összetartozó függvényeket és változókat külön helyre, ún. osztályokba tudjuk szervezni. Például:
class Person: def __init__(self, name, age): self.name = name self.age = age def print_info(self): print(self.name + ' vagyok, ' + str(self.age) + ' éves.') person1 = Person('Pista', 17) person2 = Person('Jóska', 46) person3 = Person('Marika', 23) print(person1.name) print(person1.age) person1.print_info() person2.print_info() person3.print_info()
Ennek eredménye:
Pista
17
Pista vagyok, 17 éves.
Jóska vagyok, 46 éves.
Marika vagyok, 23 éves.
Vizsgáljuk meg a fenti kódot!
- A class kulcsszóval indul.
- Utána jön az osztály neve. Ezt általában nagy kezdőbetűvel írjuk. Ebben a példában ez a Person, ami személyt jelent angolul.
- Az első sor itt is kettősponttal zárul, és a belseje ugyanúgy beljebb van húzva, mint az elágazásoknál és a ciklusoknál.
- A def __init__() részt létrehozónak, idegen szóval konstruktornak hívjuk. Az init az angol initialize szó rövidítése, ami kezdeti beállítást jelent. Ennek mindenképpen ez kell, hogy legyen a neve.
- Az első paraméter "foglalt"; általában self.
- A példában van még két paraméter: name (név) és age (életkor).
- A self.name = name és a self.age = age elmentik a paraméterül kapott értékeket, hogy később is fel tudjuk használni. A self után megadott értékeket adatmezőknek hívjuk.
- A def print_info(self): a Person osztály másik függvénye. Itt is kötelezően meg kellett adni azt, hogy self, egyébként ennek a példában nincs paramétere.
- A print(self.name + ' vagyok, ' + str(self.age) + ' éves.') kiírja az illető nevét, és azt, hogy hány éves. Figyeljük meg azt, hogy a self-en keresztül érjük el azt, amit az __init__() függvényben beállítottunk!
- person1 = Person('Pista', 17), person2 = Person('Jóska', 46) és person3 = Person('Marika', 23): ezeket példányosításnak hívjuk: konkrét értékeket adnunk nekik.
- person1.name és person1.age: így tudjuk az egyes adatmezőket elérni kívülről, közvetlenül.
- A person1.print_info() (és a másik kettő) azt mutatja meg, hogy hogyan tudunk függvényt hívni egy osztályon. Figyeljük meg, hogy mindegyik függvény a saját információit írja ki.
Általában az adatmezőket kívülről nem szoktuk közvetlenül használni, hanem csak függvényeken keresztül.
Osztályok a Scratch-ben külön nincsenek, ám egész jól lehet a lehetőséget szimulálni a másolatot készítő utasításokkal, valamint olyan változókkal, amelyek a kiválasztott szereplőhöz tartoznak.
Öröklődés
Az osztályoknál egy nagyon érdekes lehetőség az úgynevezett öröklődés. Ennek az a lényege, hogy amikor létrehozunk egy osztályt, akkor zárójelben megadhatunk egy másikat, és automatikusan megkapja annak az adatmezőit és függvényeit. Amiből származtatunk, azt ős osztálynak, míg amit származtatunk, azt leszármazott osztálynak hívjuk. Olyan osztályból is lehet örökölni, ami másikból örökölt, így tetszőleges mélységű öröklődési fát tudunk létrehozni.
Lássunk egy példát! Hozzunk létre egy ősosztályt!
class Shape: def get_type(self): return 'Síkidom' def get_perimeter(self): return 0 def get_area(self): return 0 def print_data(self): my_type = self.get_type() my_perimeter = self.get_perimeter() my_area = self.get_area() print(f'{my_type} vagyok. A kerületem: {my_perimeter}, a területem: {my_area}.')
Létrehoztunk tehát egy Shape (síkidom) osztályt, aminek négy függvénye van:
- get_type(): visszaadja a síkidom típusát (type = típus). Ez egyelőre jobb híján az, hogy síkidom.
- get_perimeter(): visszaadja a síkidom kerületét (perimeter = kerület); jelenleg jobb híján nullát.
- get_area(): visszaadja a síkidom területét (area = terület); jelenleg az is jobb híján nullát.
- print_data(): kiírja az adatokat.
Most származtassunk le a fenti osztályból! Hozzunk létre egy kört (angolul circle)!
class Circle(Shape): def __init__(self, radius): self.radius = radius def get_type(self): return 'Kör' def get_perimeter(self): return 2 * self.radius * 3.14 def get_area(self): return self.radius * self.radius * 3.14
Lássuk a részleteket!
- Az osztály paraméterként megkapja a kör sugarát (radius).
- A get_type() azt adja vissza, hogy kör.
- A kerület képlete: $2\cdot r\cdot\pi$, ezt megvalósítottuk a get_perimeter() függvényben. Tehát felüldefiniáltuk az ős osztály ugyanilyen nevű függvényét.
- A terület képlete: $r^2\cdot\pi$, ezt megvalósítottuk a get_area() függvényben. Ezt is felüldefiniáltuk.
- A print_data() itt nincs, azt örököljük úgy, ahogy van.
Próbáljuk ki!
my_circle1 = Circle(3) my_circle2 = Circle(4) my_circle1.print_data() # Kör vagyok. A kerületem: 18.84, a területem: 28.26. my_circle2.print_data() # Kör vagyok. A kerületem: 25.12, a területem: 50.24.
Tehát anélkül tudtuk használni a print_data() függvényt, hogy azt létrehoztuk volna a Circle osztályban. Örököltük a Shape osztályból, az általa hívott függvényeket felülírtuk, így a megfelelő értékeket írt ki, nem nullákat.
Most hozzunk létre egy másik síkidomot, egy téglalapot (angolul rectangle)!
class Rectangle(Shape): def __init__(self, a, b): self.a = a self.b = b def get_type(self): return 'Téglalap' def get_perimeter(self): return 2 * (self.a + self.b) def get_area(self): return self.a * self.b
Itt két paramétert kap, a téglalap két oldalát, amit a-val és b-vel jelölünk.
- get_type(): azt adja vissza, hogy téglalap.
- get_perimeter(): a téglalap kerülete $2\cdot(a+b)$.
- get_area: a téglalap területe $a\cdot b$.
Ezt is próbáljuk ki!
my_rectangle1 = Rectangle(2, 3) my_rectangle2 = Rectangle(3, 4) my_rectangle1.print_data() # Téglalap vagyok. A kerületem: 10, a területem: 6. my_rectangle2.print_data() # Téglalap vagyok. A kerületem: 14, a területem: 12.
Most hozzunk létre egy négyzetet (sqaure)! Valójában nem kell közvetlenül a Shape-ből származtatni, lehet a Rectangle-ből is, és akkor megspórolunk egy csomó dolgot:
class Square(Rectangle): def __init__(self, a): self.a = a self.b = a def get_type(self): return 'Négyzet'
A négyzetnek csak egy paramétere van, az oldal hossza, amit jelöljünk a-val. Mivel a Rectangle osztályból öröklünk, van a és b adatmezőnk. A konstruktorban állítsuk be mind a kettő a-ra. Ezen kívül valósítsuk meg a get_type() függvényt. Minden más marad az eredeti: a get_perimeter() és a get_area() a téglalapból öröklődik, míg a print_data() a "nagyszülő" síkidomból. Próbáljuk ki!
my_square1 = Square(3) my_square2 = Square(5) my_square1.print_data() # Négyzet vagyok. A kerületem: 12, a területem: 9. my_square2.print_data() # Négyzet vagyok. A kerületem: 20, a területem: 25.
Feladat
Készítsük el a kvíz osztályokkal megvalósított változatát! Egy osztály tartalmazza a következőket:
- A kérdés.
- A helyes válasz.
- Egy függvény, ami kiírja a kérdést, bekéri a választ, és megfelelően kezeli azt.
Példányosítsunk 5 kérdést, tegyük bele egy listába, és azokon lépegessünk végig.
class Question: def __init__(self, question, correct_answer): self.question = question self.correct_answer = correct_answer def check_answer(self): answer = input(self.question) if answer == self.correct_answer: print('Helyes!') return True else: print(f'A válasz helytelen; a helyes válasz {self.correct_answer} lett volna.') return False questions = [] questions.append(Question('Mi Magyarország fővárosa? ', 'Budapest')) questions.append(Question('Hány fokon forr a víz? ', '100')) questions.append(Question('Melyik az az állat, amelyiknek nagy füle és hosszú ormánya van? ', 'elefánt')) questions.append(Question('Hogy mondjuk angolul azt, hogy alma? ', 'apple')) questions.append(Question('Mi volt Petőfi vezetéknevű költőnk keresztneve? ', 'Sándor')) points = 0 for question in questions: result = question.check_answer() if result: points = points + 1 print() percentage = str(100 * points / len(questions)) + '%' print('Az eredmény: ' + percentage)
Önálló feladat
Egészítsük ki a Question osztályt egy típus adatmezővel. A típus lehet szabad szöveg (mint fent), szám, több válaszlehetőségből egy stb. A check_answer függvényt ennek alapján módosítsuk.
Teszt
1. feladat
Mit csinál az alábbi kód?
class MyMath: def __init__(self, x, y): self.a = x self.b = y def my_add(self): return self.a + self.b def my_multiply(self): return self.a * self.b my_math = MyMath(2, 3) print(my_math.my_add()) print(my_math.my_multiply())
A Kiírja, hogy 5 és 6, külön sorban.
B Hibát ír ki, mert az __init__() függvényben x és y szerepel, az osztály változó viszont self.a és self.b.
C Hibát ír ki, mert a my_add() és my_multiply() függvényeknek meg kell adni azt, hogy mit szeretnénk összeadni ill. összeszorozni.
D Hibát ír ki, mert a print() paramétereként nem my_math-ot kellene írni, hanem azt, hogy MyMath, mivel ez utóbbi az osztály neve.
2. feladat
Mit csinál az alábbi kód?
class MyAdd: def __init__(self, x, y): self.a = x self.b = y def my_add(self): return self.a + self.b class MyMultiply(MyAdd): def my_multiply(self): return self.a * self.b my_math = MyMultiply(2, 3) print(my_math.my_add()) print(my_math.my_multiply())
A Kiírja, hogy 5 és 6, külön sorban.
B Hibát ír ki, mert a my_math = MyMultiply(2, 3) sorban, mert a példánynak azt a nevet kell adni, hogy my_multiply, és nem azt, hogy my_math, mivel az osztály neve az, hogy MyMultiply.
C Hibát ír ki, mert MyMultiply osztálynak (amilyen típusú a my_math) nincs my_add() függvénye.
D Hibát ír ki, mert a my_multiply() függvény nem láthatja a self.a és self.b változókat, mert az a MyAdd-ban hoztuk létre.
3. feladat
Mit csinál az alábbi kód?
class MyBase: def __init__(self, a, b): self.a = b self.b = a class MyMath(MyBase): def my_add(self): return self.a + self.b def my_multiply(self): return self.a * self.b my_math = MyBase(2, 3) print(my_math.my_add()) print(my_math.my_multiply())
A Kiírja, hogy 5 és 6, külön sorban.
B Hibás eredményt ír ki.
C Hibát ír ki, mert a my_math nem találja a my_add() és my_multiply() függvényeket.
D Hibát ír ki, mert a konstruktorban fel van cserélve az a és a b.