Ha eddig funkcionális, deklaratív vagy imperatív stílusban programoztál, az objektumorientált programozásra (OOP) való váltás eleinte kicsit idegennek tűnhet.
Az OOP-megközelítés arra kéri a programozót, hogy a problémát egy vagy több objects formájában modellezze, amelyek kölcsönhatásba lépnek egymással, állapotot tartanak fenn, és műveleteket végeznek az adatokon.
Az objektumok ábrázolhatnak valós világbéli entitásokat (például autókat vagy macskákat), vagy elvontabb fogalmakat (például egész számokat, járműveket vagy emlősöket).
Minden objektum egyedi példányként jön létre a számítógép memóriájában, és a teljes modell valamely részét képviseli.
A Classes az új objektumtípusok definíciói, és ezekből jönnek létre az objektumok új instances-ei.
Gyakran az adatokat az ezeken az adatokon működő kóddal vagy függvényekkel csomagolják egybe.
Ebben az értelemben az osztályok tervrajzok, illetve utasításhalmazok, amelyekből sok hasonló típusú objektum építhető fel és használható.
Egy összetett programnak sok osztálya lehet, és mindegyik sokféle objektumot építhet.
Az objektumnak egy osztályból való létrehozásának folyamatát instantiation néven ismerjük (vagyis az osztály egy példányának létrehozásaként).
Egy osztálydefiníció Pythonban egyszerű:
class MyClass:
# Class body goes here
Az osztálymezőket (más néven properties, attributes, data members vagy variables) hozzáadhatod az osztály törzséhez:
class MyClass:
number = 5
string = "Hello!"
A MyClass egy példánya (objektuma) létrehozható és egy névhez köthető úgy, hogy meghívod az osztályt (ugyanúgy, ahogy egy függvényt szoktál):
>>> new_object = MyClass()
# Class is instantiated and resulting object is bound to the "new_object" name.
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>
A Class attributes az osztályból létrehozott összes objektum (vagy példány) között megoszlanak, és dot notation segítségével érhetők el - egy . karakterrel az objektum neve után és az attribútum neve előtt:
>>> new_object = MyClass()
# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5
# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'
# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()
>>> second_new_object
# Note: the object address "at 0x15ad99970" will vary by computer.
<__main__.MyClass at 0x15ad99970>
# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True
Az osztályattribútumok magának az osztálynak a törzsében, minden más metódus előtt vannak definiálva. Az osztály birtokolja őket, ami lehetővé teszi, hogy az osztály példányai között megoszlanak. Mivel ezek az attribútumok megosztottak, értékük az osztályból közvetlenül is elérhető és módosítható. Ha megváltoztatod egy osztályattribútum értékét, az az osztályból létrehozott összes objektum értékét is megváltoztatja:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
# Accessing a class attribute from an object.
>>> obj_two.number
5
# Accessing the class attribute from the class itself.
>>> MyClass.number
5
# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27
# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27
>>> obj_two.number
27
Nem túl hasznos, ha egy csomó objektum adatai minden pillanatban szinkronban vannak.
Szerencsére az osztályból létrehozott objektumok saját instance attributes-szal (vagy példánytulajdonságokkal, példányváltozókkal vagy példánymezőkkel) testre szabhatók.
Ahogy a nevük is sugallja, a példányattribútumok minden objektumhoz egyediek, és egymástól függetlenül módosíthatók.
__init__() metódussalA speciális „dunder metódus” (a „double underscore method”, azaz a dupla aláhúzásos metódus rövidítése) __init__() az osztálypéldányok testreszabására szolgál, és arra használható, hogy inicializálja az objektumok példányattribútumait vagy tulajdonságait.
Mivel a példányattribútumok inicializálásában játszik szerepet, a __init__()-t class constructor-nak vagy initializer-nek is nevezik.
A __init__() egy kötelező paramétert vár, amelynek neve self, és az éppen inicializált vagy létrehozott objektumra utal.
A példányattribútumok vagy tulajdonságok adatai ezután a self paramétert követően a __init__() argumentumaiként adhatók át.
Az alábbi példában a MyClass-nak most location_x és location_y nevű példányattribútumai vannak.
Amint látod, a két attribútum a __init__()-nak átadott location (egy tuple) argumentum első és második indexéhez lett rendelve.
Az osztály egy példányának location_x és location_y attribútumai most inicializálódnak, amikor példányosítod az osztályt, és létrejön egy objektum:
class MyClass:
# These are class attributes, variables, or fields.
number = 5
string = "Hello!"
# This is the class "constructor", with a "location" parameter that is a tuple.
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
# Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))
# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))
# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1
>>> new_object_two.location_x
-8
Figyeld meg, hogy a fenti MyClass inicializálásakor csak egy argumentumot kell átadnod, a self átadásáról a Python gondoskodik, amikor az osztályt meghívod.
A method egy function, amely vagy magához az osztályhoz (ezt osztálymetódusnak nevezik, és egy későbbi feladatban lesz róla szó), vagy az osztály egy példányához (objektumához) kötődik.
Azokat a metódusokat, amelyek egy objektumon (példányon) működnek, úgy kell definiálni, hogy az első paraméterük a self legyen.
A többi paramétert ezután ugyanúgy definiálhatod, mint egy „normál”, nem kötött függvény esetében:
class MyClass:
number = 5
string = "Hello!"
# Class constructor.
def __init__(self, location):
# Instance properties
self.location_x = location[0]
self.location_y = location[1]
# Instance method. Note "self" as first parameter.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
Az attribútumok eléréséhez hasonlóan egy metódus meghívásához egyszerűen csak egy . kell az objektum neve után és a metódus neve elé.
A meghívott metódusnak nem kell első paraméterként megkapnia az objektum másolatát, mert a self-et a Python automatikusan kitölti:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)
# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)
Az osztályattribútumok a példánymetódusokon belülről ugyanúgy érhetők el, ahogy az osztályon kívülről:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5
>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6
A korábbi tanulófeladatokban és a gyakorlófeladatok vázaiban láthattad, hogy a pass kulcsszót a függvények törzsében, a tényleges kód helyén használjuk.
A pass kulcsszó egy szintaktikailag érvényes helyőrző: megakadályozza, hogy a Python szintaktikai hibát dobjon egy üres függvény- vagy osztálydefiníció miatt.
Lényegében ez egy módja annak, hogy azt mondd a Python értelmezőnek: „Ne aggódj! Ide fog végül kód kerülni, csak még nem tettem meg.”
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
# Stubbing or placholding the body of this method.
pass
Ellen egy olyan játékot készít, amelyben a játékosnak alienekkel kell harcolnia.
Éppen most tanult az objektumorientált programozásról (OOP), és alig várja, hogy kihasználja, mit nyújthat a programjának a classes használata.
Ellen örömére felajánlottad a segítségedet, ő pedig rád bízta, hogy programozd le azokat az alieneket, amelyekkel a játékosnak harcolnia kell.
Definiáld az Alien osztályt egy konstruktorral, amely két paramétert fogad, <x_coordinate> és <y_coordinate>, és ezeket az x_coordinate, illetve y_coordinate példányváltozókba teszi.
Minden alien 3-as életerőszinttel is indul, tehát a health változót is inicializálni kell.
>>> alien = Alien(2, 0)
>>> alien.x_coordinate
2
>>> alien.y_coordinate
0
>>> alien.health
3
Így minden alien képes lesz belsőleg nyilvántartani a saját pozícióját és életerejét.
hit metódusEllen szeretné, ha az Alien class-nek lenne egy hit metódusa, ami meghívásakor 1-gyel csökkenti egy alien objektum életerejét.
Így egyszerűen csak <alien>.hit()-et kell hívnia, ahelyett hogy kézzel módosítaná az alien életerejét.
Rajtad áll, hogy a hit() az életerőpontokat nullára vagy az alá csökkenti-e.
>>> alien = Alien(0, 0)
# Initialized health value.
>>> alien.health
3
# Decrements health by 1 point.
>>> alien.hit()
>>> alien.health
2
is_alive metódusRájössz, hogy ha az életerő folyamatosan csökken, akkor egy ponton valószínűleg eléri a 0-t (vagy akár annál is kevesebbet!).
Jó ötlet lenne hozzáadni egy is_alive metódust, amit Ellen gyorsan meghívhat, hogy ellenőrizze, vajon az alien... nos... életben van-e. 😉
A <alien>.is_alive() egy boolean értéket adjon vissza.
>>> alien.health
1
>>> alien.is_alive()
True
>>> alien.hit()
>>> alien.health
0
>>> alien.is_alive()
False
teleport metódusEllen játékában az alienek képesek teleportálni!
Írnod kell egy teleport metódust, amely új x_coordinate és y_coordinate értékeket vesz át, és ennek megfelelően módosítja az alien koordinátáit.
>>> alien.teleport(5, -4)
>>> alien.x_coordinate
5
>>> alien.y_coordinate
-4
collision_detection metódusNyilvánvaló, hogy ha az alieneket eltalálhatja valami, akkor fel kell tudniuk ismerni, amikor ilyen ütközés történik.
Az ütközésészlelő algoritmusok viszont bonyolultak lehetnek, és te még nem tudod, hogyan kell megvalósítani egyet.
Ellen azt mondta, hogy később megcsinálja, de szeretné, ha a collision_detection metódus mégis megjelenne az osztályban, emlékeztetőül arra, hogy ki kell dolgozni a funkcionalitást.
Valamilyen változót kell átvennie (valószínűleg egy másik objektumot), de valójában csak ennyit tudsz.
Gondoskodnod kell róla, hogy a metódusdefiníció osztályba helyezése ne okozzon hibát, amikor meghívják:
>>> alien.collision_detection(other_object)
>>>
Ellen egy új kéréssel tért vissza hozzád. Szeretné számon tartani, hány alien jött létre a játék teljes időtartama alatt. Azt mondja, ennek köze van a pontozási rendszerhez.
Például:
>>> alien_one = Alien(5, 1)
>>> alien_one.total_aliens_created
1
>>> alien_two = Alien(3, 0)
>>> alien_two.total_aliens_created
2
>>> alien_one.total_aliens_created
2
# Accessing the variable from the class directly
>>> Alien.total_aliens_created
2
Ellen imádja, amit eddig csináltál, de még egy szívességet kérne.
Szeretne egy önálló (az Alien() osztályon kívül álló) függvényt, amely pozíciók listájából (tuples formában megadva) Alien() objektumokból álló list-et készít.
Például:
>>> alien_start_positions = [(4, 7), (-1, 0)]
>>> aliens = new_aliens_collection(alien_start_positions)
...
>>> for alien in aliens:
... print(alien.x_coordinate, alien.y_coordinate)
(4, 7)
(-1, 0)
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Python nyelvet 17 fogalom146 feladat segítségével, valódi emberi mentorálással, mindez ingyen.