There are four basic principles of OOP. They are encapsulation, abstraction, inheritance, and polymorphism.
-
Data encapsulation is the mechanism of hiding the internal data of objects from the world. All interaction with the object and its data are performed through its public methods. Encapsulation allows programmers to protect the object from inconsistency.
-
Data abstraction means that objects should provide the simplified, abstract version of their implementations. The details of their internal work usually aren’t necessary for the user, so there’s no need to represent them. Abstraction also means that only the most relevant features of the object will be presented.
-
Inheritance is a mechanism for defining parent-child relationships between classes. Often objects are very similar, so inheritance allows programmers to reuse common logic and at the same time introduce unique concepts into the classes.
-
Polymorphism literally means one name and many forms, and it concerns the inheritance of the classes. Just as the name suggests, it allows programmers to define different logic of the same method. So, the name (or interface) stays the same, but the actions performed may be different. In practice, it is done with overloading or overriding.
KLASA - jest “ideą”, “schematem”, “wyobrażeniem” właściwości (zmienne) i interfejs (metody)
INSTANCJA - jest “powołanym do życia” obiektem, który zawiera określone przez klasę właściwości. Można mieć kilka instancji jednej klasy
Do stworzenia klasy potrzebujemy:
-
nazwy klasy
-
zdefiniować właściwości klasy
Używanie klasy polega na:
- utworzeniu nowej instancji obiektu
- wykonywaniu operacji na instancji
Zalety OOP:
- tworzenie jednorodnego pakietu, zawierającego dane oraz sposoby manipulowania nimi
- umożliwiają podejście - divide and conquer (dziel i zwyciężaj)
- można testować zachowanie każdej z klas oddzielnie
- zwiększa modularność, zmniejsza kompleksowość
- klasy ułatwiają ponowne użycie kodu
-
każda z klas tworzy oddzielne “środowisko” - różne klasy mogą mieć takie same nazwy funkcji
- dziedziczenie pozwala aby podklasa, zredefiniowała lub rozszerzyła wybrane właściwości klasy nadrzędnej
Samochody mają dane, jak na przykład liczba kół, drzwi, oraz ilość miejsc siedzących. Wykazują one również zachowanie: mogą przyspieszyć, zatrzymać się, pokazać ile paliwa zostało oraz wiele innych.
W programowaniu object-oriented dane identyfikujemy jako atrybuty, a zachowanie jako metody.
Dane → Atrybuty i Zachowanie → Metody
Przykład prostej klasy:
class Samochod(object):
# definicje danych
# definicje metod
class – podobnie jak def
- słowo object oznacza, że Samochód jest obiektem w Python (object) i dziedziczy z niego wszystkie właściwości
- Samochod jest podklasą object
- object jest klasą nadrzędną dla Samochod
Definiowanie konstruktora:
class Samochod(object):
def __init__(self,marka,model) # double under - dunder
self.marka = marka # atrybuty każdej instancji obiektu Samochod
self.model = model
self - referencja instancji
marka, model - dane inicjalizujące
Definiowanie metod:
def accelerate(self, value):
self.speed += value
Metoda a funkcja:
# class and its methods
class Ship:
def __init__(self, name, capacity):
self.name = name
self.capacity = capacity
self.cargo = 0
def sail(self):
print("{} has sailed!".format(self.name))
# function
def sail_function(name):
print("{} has sailed!".format(name))
OPERATORY:
+, -, ==, <, >, len(), print, i in.
__add__(self, other) # -> self + other
__sub__(self, other) # -> self -other
__eq__(self, other) # -> self == other
__lt__(self, other) # -> self < other
__len__(self) # -> len(self)
__str__(self) # -> print(self)
Paradygmaty OOP:
- abstrakcja – uproszczenie problemu
- enkapsulacja (HERMETYZACJA) – ukrycie składowych
- dziedziczenie – mechanizm współdzielenia funkcjonalności
- polimorfizm – wielopostaciowość
Dziedziczenie
Dziedziczenie umożliwia tworzenie klas, które korzystają z atrybutów klas nadrzędnych (superklasa / rodzic). Klasy dziedziczące (podklasy / dzieci) mogą część atrybutów mieć zdefiniowanych według własnych potrzeb.
class Zwierze(object):
# definicje danych
# definicje metod
class Czlowiek(Zwierze):
# definicje danych
# definicje metod
class Student(Czlowiek):
# definicje danych
# definicje metod
isinstance(obiekt, klasa) # sprawdza czy dany obiekt jest instancją klasy
issubclass(klasaA, klasaB) # sprawdza czy klasaA jest podklasą klasy B
Klasa może dziedziczyć z wielu klas
Pola klasy - Zmienne definiowane na poziomie klasy. Nie używamy słówka self
Służą do przechowywania danych niezależnych od instancji (wspólne dla wszystkich instancji)
Metody klasy - Metody, które jako pierwszy argument przyjmują klasę zamiast instancji.
Używamy dekoratora @classmethod nad definicją metody.
Pierwszy argument to słowo kluczowe cls Możemy używać jako alternatywne konstruktory
@classmethod
def my_class_method(cls):
pass
Metody statyczne - Metody, które nie przyjmują ani instancji ani klasy jako argument. Wyglądają jak normalne metody
Używamy dekoratora @staticmethod nad definicją metody.
Używamy je gdy przekazanie jakiejś informacji nie wymaga tworzenia instancji klasy. (matematyczne)
@staticmethod
def my_static_method():
pass
MyClass.my_static_method()
enkapsulacja
Python daje możliwość stworzenia pseudo-prywatnych pól i metod.
Do nazwy (pola, metody) dodajemy dwa podkreślniki tylko z przodu. Można je użyć wewnątrz klasy, ale poza nią są niewidoczne – ale można i tak ich użyć!
self.__moje_pole_prywatne
def __metoda_prywatna(self, arg1):
self.__moje_pole_prywatne = arg1
namespace
Namespace jest obszarem nazw, które są dostępne dla klasy. W ten sposób możemy znaleźć pseudo-prywatny atrybut
print(MojaKlasa.__dict__)
print(instancja.__dict__)
Properties – właściwości definiujemy jak metody z dekoratorem, z nazwą identyczną jak zmienna, służą do manipulowania zmiennymi w kontrolowany przez nas sposób.
Używamy jak zmienne!
Getter – służy do zwrócenia wartości ze zmiennej
self.__imie
@property
def imie(self):
return str(self.__imie).capitalize()
Setter – służy do zapisania wartości do zmiennej – daje możliwość do kontrolowania tego co zapisujemy (musi najpierw być zdefiniowany getter)
self.__imie
@imie.setter
def name(self, value):
self.__imie = value
Deleter – służy do usuwania zawartości zmiennej w kontrolowany sposób
self.__imie
@imie.deleter
def name(self):
self.__imie = None
class Patient(object):
''' Medical centre patient'''
status = 'patient'
def __init__(self,name,age):
self.name = name
self.age = age
self.conditions = []
def get_details(self):
print(f'Patient record: {self.name}, {self.age} years.' \
f' Current information: {self.conditions}.')
def add_info(self,information):
self.conditions.append(information)
Stworzenie obiektów:
steve = Patient('Steven Hughes',48)
abigail = Patient('Abigail Sandwick',32)
class Infant(Patient):
''' Patient under 2 years'''
def __init__(self,name,age):
self.vaccinations = []
super().__init__(name,age)
def add_vac(self,vaccine):
self.vaccinations.append(vaccine)
def get_details(self):
print(f'Patient record: {self.name}, {self.age} years.' \
f' Patient has had {self.vaccinations} vaccines.' \
f' Current information: {self.conditions}.' \
f'\n{self.name} IS AN INFANT, HAS HE HAD ALL HIS CHECKS?')
Stworzenie “dzieci”:
archie = Infant('Archie Fittleworth',0)
archie.add_vac('MMR')
class Patient(object):
'''
Attributes
----------
name: Patient name
age: Patient age
conditions: Existing medical conditions
'''
status = 'patient'
def __init__(self,name,age):
self.name = name
self.age = age
self.conditions = []
def get_details(self):
print(f'Patient record: {self.name}, {self.age} years.' \
f' Current information: {self.conditions}.')
def add_info(self,information):
self.conditions.append(information)