Programowanie obiektowe

Programowanie obiektowe w Pythonie

Programowanie obiektowe (OOP) to paradygmat programowania oparty na koncepcji obiektów, które zawierają dane i kod.

Klasy i obiekty:

Klasa to szablon do tworzenia obiektów. Obiekt to instancja klasy.

# Definiowanie klasy
class Dog:
    def __init__(self, name, age, breed):
        self.name = name      # Atrybut instancji
        self.age = age
        self.breed = breed
    
    def bark(self):
        return f"{self.name} szczeka: Hau! Hau!"
    
    def get_info(self):
        return f"{self.name} to {self.breed} w wieku {self.age} lat"
    
    def have_birthday(self):
        self.age += 1
        return f"{self.name} ma teraz {self.age} lat!"

# Tworzenie obiektów (instancji klasy)
my_dog = Dog("Burek", 3, "Owczarek niemiecki")
friend_dog = Dog("Luna", 2, "Golden Retriever")

# Używanie metod
print(my_dog.bark())           # Burek szczeka: Hau! Hau!
print(my_dog.get_info())       # Burek to Owczarek niemiecki w wieku 3 lat
print(my_dog.have_birthday())  # Burek ma teraz 4 lat!

print(friend_dog.get_info())   # Luna to Golden Retriever w wieku 2 lat

Konstruktor __init__:

Konstruktor jest wywoływany automatycznie przy tworzeniu obiektu.

class Person:
    def __init__(self, name, age, city):
        self.name = name
        self.age = age
        self.city = city
        self.friends = []  # Lista przyjaciół
    
    def add_friend(self, friend):
        self.friends.append(friend)
        return f"{friend} został dodany do przyjaciół {self.name}"
    
    def get_friends_count(self):
        return len(self.friends)
    
    def introduce(self):
        return f"Cześć! Jestem {self.name}, mam {self.age} lat i mieszkam w {self.city}"

# Tworzenie osób
person1 = Person("Jan", 25, "Warszawa")
person2 = Person("Anna", 28, "Kraków")

print(person1.introduce())  # Cześć! Jestem Jan, mam 25 lat i mieszkam w Warszawie
person1.add_friend("Anna")
print(person1.get_friends_count())  # 1

Atrybuty klasowe vs instancji:

class BankAccount:
    # Atrybut klasowy - wspólny dla wszystkich instancji
    bank_name = "Python Bank"
    interest_rate = 0.05
    
    def __init__(self, owner, balance=0):
        # Atrybuty instancji - unikalne dla każdego obiektu
        self.owner = owner
        self.balance = balance
        self.account_number = self._generate_account_number()
    
    def _generate_account_number(self):
        # Metoda prywatna (z podkreśleniem)
        import random
        return f"PL{random.randint(100000, 999999)}"
    
    def deposit(self, amount):
        if amount > 0:
            self.balance += amount
            return f"Wpłacono {amount} zł. Nowe saldo: {self.balance} zł"
        else:
            return "Kwota musi być większa od 0"
    
    def withdraw(self, amount):
        if amount <= self.balance:
            self.balance -= amount
            return f"Wypłacono {amount} zł. Nowe saldo: {self.balance} zł"
        else:
            return "Niewystarczające środki"
    
    def get_interest(self):
        interest = self.balance * self.interest_rate
        return f"Oprocentowanie: {interest} zł"

# Tworzenie kont
account1 = BankAccount("Jan Kowalski", 1000)
account2 = BankAccount("Anna Nowak", 500)

print(account1.deposit(500))    # Wpłacono 500 zł. Nowe saldo: 1500 zł
print(account1.withdraw(200))   # Wypłacono 200 zł. Nowe saldo: 1300 zł
print(account1.get_interest())  # Oprocentowanie: 65.0 zł

# Dostęp do atrybutów klasowych
print(BankAccount.bank_name)    # Python Bank
print(account1.bank_name)       # Python Bank (można też przez instancję)

Dziedziczenie:

Dziedziczenie pozwala na tworzenie nowych klas na podstawie istniejących.

# Klasa bazowa (rodzic)
class Animal:
    def __init__(self, name, species):
        self.name = name
        self.species = species
    
    def make_sound(self):
        return "Jakiś dźwięk"
    
    def get_info(self):
        return f"{self.name} to {self.species}"

# Klasa pochodna (dziecko)
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name, "pies")  # Wywołanie konstruktora rodzica
        self.breed = breed
    
    def make_sound(self):
        return "Hau! Hau!"
    
    def fetch(self):
        return f"{self.name} przynosi piłkę"

class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name, "kot")
        self.color = color
    
    def make_sound(self):
        return "Miau! Miau!"
    
    def climb(self):
        return f"{self.name} wspina się na drzewo"

# Tworzenie obiektów
dog = Dog("Burek", "Owczarek")
cat = Cat("Mruczek", "Rudy")

print(dog.get_info())      # Burek to pies
print(dog.make_sound())    # Hau! Hau!
print(dog.fetch())         # Burek przynosi piłkę

print(cat.get_info())      # Mruczek to kot
print(cat.make_sound())    # Miau! Miau!
print(cat.climb())         # Mruczek wspina się na drzewo

Wielokrotne dziedziczenie:

class Flyable:
    def fly(self):
        return "Lecę w powietrzu!"

class Swimmable:
    def swim(self):
        return "Pływam w wodzie!"

class Duck(Animal, Flyable, Swimmable):
    def __init__(self, name):
        super().__init__(name, "kaczka")
    
    def make_sound(self):
        return "Kwa! Kwa!"
    
    def quack_and_fly(self):
        return f"{self.make_sound()} {self.fly()}"

duck = Duck("Kaczuszka")
print(duck.make_sound())       # Kwa! Kwa!
print(duck.fly())              # Lecę w powietrzu!
print(duck.swim())             # Pływam w wodzie!
print(duck.quack_and_fly())    # Kwa! Kwa! Lecę w powietrzu!

Metody specjalne (magiczne):

class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    def __str__(self):
        return f"'{self.title}' autorstwa {self.author}"
    
    def __repr__(self):
        return f"Book('{self.title}', '{self.author}', {self.pages})"
    
    def __len__(self):
        return self.pages
    
    def __add__(self, other):
        if isinstance(other, Book):
            return Book(f"{self.title} + {other.title}", 
                       f"{self.author} + {other.author}", 
                       self.pages + other.pages)
        return "Nie można dodać książki do tego typu obiektu"
    
    def __eq__(self, other):
        if isinstance(other, Book):
            return (self.title == other.title and 
                   self.author == other.author and 
                   self.pages == other.pages)
        return False

book1 = Book("Python dla początkujących", "Jan Kowalski", 300)
book2 = Book("Zaawansowany Python", "Anna Nowak", 400)

print(str(book1))          # 'Python dla początkujących' autorstwa Jan Kowalski
print(repr(book1))         # Book('Python dla początkujących', 'Jan Kowalski', 300)
print(len(book1))          # 300
print(book1 + book2)       # Book('Python dla początkujących + Zaawansowany Python', 'Jan Kowalski + Anna Nowak', 700)
print(book1 == book2)      # False

Właściwości (Properties):

class Circle:
    def __init__(self, radius):
        self._radius = radius  # Atrybut prywatny
    
    @property
    def radius(self):
        return self._radius
    
    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Promień nie może być ujemny")
        self._radius = value
    
    @property
    def area(self):
        import math
        return math.pi * self._radius ** 2
    
    @property
    def circumference(self):
        import math
        return 2 * math.pi * self._radius

circle = Circle(5)
print(f"Promień: {circle.radius}")           # Promień: 5
print(f"Pole: {circle.area:.2f}")            # Pole: 78.54
print(f"Obwód: {circle.circumference:.2f}")  # Obwód: 31.42

circle.radius = 10  # Ustawienie nowego promienia
print(f"Nowe pole: {circle.area:.2f}")       # Nowe pole: 314.16

# circle.radius = -5  # Błąd: Promień nie może być ujemny

Dobre praktyki OOP:

  • Używaj opisowych nazw klas i metod
  • Zachowaj enkapsulację (ukryj szczegóły implementacji)
  • Używaj dziedziczenia do modelowania relacji „jest”
  • Preferuj kompozycję nad dziedziczeniem
  • Dokumentuj swoje klasy i metody
  • Używaj właściwości zamiast getterów/setterów
  • Implementuj metody specjalne gdy potrzeba

Ćwiczenie:

Stwórz klasę Student z atrybutami (imię, nazwisko, oceny) i metodami (dodaj ocenę, oblicz średnią, wyświetl informacje).

Brak odpowiedzi

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *