Streszczenie
Atrybuty instancji i klasowe, metody instancji oraz zarządzanie stanem obiektu

Ten moduł wprowadza kluczowe koncepcje programowania obiektowego w Pythonie - atrybuty instancji i klasowe oraz metody instancji. Poznasz różnicę między stanem indywidualnym obiektu a danymi współdzielonymi na poziomie klasy, a także mechanizm przesłaniania (shadowing) atrybutów. Nauczysz się definiować metody odczytujące i modyfikujące stan obiektu za pomocą self , a także korzystać z __dict__ do introspekcji atrybutów. Omówione zostaną wbudowane funkcje zarządzania atrybutami ( hasattr , getattr , setattr , del ) oraz praktyczny wzorzec Licznika z atrybutem klasowym jako globalnym licznikiem instancji. Materiał zawiera 50 slajdów z przykładami kodu, diagramami i quizem sprawdzającym wiedzę.

  • Atrybuty instancji - definiowane w __init__ przez self , każdy obiekt ma własną, niezależną kopię stanu
  • Atrybuty klasowe - współdzielone przez wszystkie instancje, modyfikacja przez klasę wpływa na wszystkie obiekty
  • Metody instancji - odczyt i modyfikacja atrybutów przez self , wywoływanie innych metod, enkapsulacja stanu
  • Introspekcja atrybutów - __dict__ , hasattr , getattr , setattr , del oraz mechanizm shadowingu
  • Praktyczny przykład Licznika - atrybut klasowy jako licznik obiektów, atrybuty instancji jako unikalny stan indywidualny
Streszczenie modułu

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

1 / 50
Wprowadzenie

Atrybuty instancji i klasowe, metody instancji, odczyt i modyfikacja stanu

W tej części poznasz różnicę między atrybutami instancji a atrybutami klasowymi, nauczysz się definiować metody instancji oraz odczytywać i modyfikować stan obiektu. Zrozumiesz, jak Python zarządza danymi na poziomie klasy i na poziomie pojedynczego obiektu - to fundament programowania obiektowego, który pozwala pisać czysty, modułowy i skalowalny kod.

Atrybuty to zmienne należące do obiektu lub klasy. Metody to funkcje działające na tych atrybutach. Razem tworzą spójny interfejs do zarządzania danymi. Znajomość różnic między atrybutami instancji i klasowymi jest kluczowa - błędne użycie prowadzi do subtelnych bugów trudnych do wyśledzenia.

Materiał zawiera 50 slajdów z przykładami kodu, diagramami i ćwiczeniami. Każdy slajd buduje wiedzę krok po kroku, a przykłady są zaprojektowane tak, by można je było uruchomić samodzielnie w interpreterze Pythona.

Tytułowa

Moduł ten stanowi pierwszą część kompleksowego kursu programowania obiektowego w języku Python. Jego celem jest wprowadzenie studentów w paradygmat obiektowy oraz przedstawienie fundamentalnych koncepcji, takich jak klasy, obiekty, atrybuty i metody. Materiał został zaprojektowany tak, aby stopniowo budować zrozumienie od prostych analogii do konkretnych implementacji w kodzie. Każdy slajd zawiera przykłady, które można samodzielnie przetestować w środowisku REPL. Zaleca się aktywne uczestnictwo poprzez modyfikowanie przykładów i wykonywanie ćwiczeń. Systematyczna praca z materiałem gwarantuje solidne opanowanie podstaw OOP. W kolejnych modułach wiedza ta będzie rozwijana o bardziej zaawansowane zagadnienia.

Programowanie obiektowe to nie tylko zestaw reguł składniowych, ale przede wszystkim sposób myślenia o problemach programistycznych. Kluczowe jest zrozumienie, że klasy służą do modelowania rzeczywistych bytów i relacji między nimi. Dzięki OOP kod staje się bardziej modularny, łatwiejszy w utrzymaniu i bardziej odporny na błędy. Współczesne aplikacje webowe, systemy bazodanowe i frameworki w dużym stopniu opierają się na paradygmacie obiektowym. Opanowanie OOP otwiera drzwi do zrozumienia zaawansowanych wzorców projektowych. Zachęcamy do cierpliwej i systematycznej nauki - każde nowe pojęcie będzie szczegółowo wyjaśnione i zilustrowane przykładami.

2 / 50
Cele dydaktyczne

Po tej części będziesz umiał:

  • Definiować atrybuty instancji w metodzie __init__ za pomocą self
  • Definiować atrybuty klasowe bezpośrednio w ciele klasy, poza metodami
  • Wyjaśnić różnicę między atrybutem instancji a klasowym - w tym różnicę w pamięci i widoczności
  • Tworzyć metody instancji odczytujące i modyfikujące stan obiektu
  • Używać __dict__ do podglądu atrybutów obiektu i klasy
  • Stosować wbudowane funkcje del , hasattr , getattr , setattr do zarządzania atrybutami
  • Rozumieć mechanizm przesłaniania (shadowing) atrybutu klasowego przez instancyjny
  • Projektować klasy z użyciem atrybutów klasowych jako wspólnych liczników lub stałych
Cele

Przedstawione cele dydaktyczne zostały opracowane zgodnie z zasadą stopniowania trudności, co pozwala na systematyczne budowanie kompetencji. Każdy cel koncentruje się na konkretnym aspekcie programowania obiektowego, od teorii przez praktykę aż po zaawansowane zastosowania. Realizacja wszystkich założeń gwarantuje solidne podstawy do dalszego rozwoju w kierunku bardziej złożonych zagadnień, takich jak wzorce projektowe. Materiał został dostosowany do potrzeb studentów kierunków informatycznych. Oczekuje się, że po ukończeniu modułu student będzie potrafił samodzielnie projektować proste hierarchie klas i implementować je w Pythonie. Regularne sprawdzanie postępów pomoże w identyfikacji obszarów wymagających dodatkowej uwagi i powtórki.

Struktura kursu przewiduje płynne przejście od zagadnień podstawowych do bardziej złożonych, z licznymi przykładami i ćwiczeniami praktycznymi. Zaleca się aktywne uczestnictwo poprzez samodzielne eksperymenty z kodem. Środowisko REPL Pythona jest doskonałym narzędziem do natychmiastowego testowania omawianych koncepcji bez konieczności tworzenia plików projektowych. Warto również korzystać z dodatkowych materiałów, takich jak dokumentacja oficjalna Pythona oraz społecznościowe fora programistyczne. Systematyczna praca i regularne powtórki są kluczem do sukcesu w opanowaniu programowania obiektowego.

3 / 50
Atrybuty instancji - wprowadzenie

Definiowanie w __init__ przez self

Atrybuty instancji są definiowane wewnątrz metody __init__ przy użyciu parametru self . Każdy obiekt otrzymuje własną kopię tych atrybutów w momencie tworzenia - to znaczy, że dwa obiekty tej samej klasy mogą mieć całkowicie różne wartości atrybutów instancji.

Składnia:self.nazwa_atrybutu = wartość

Atrybuty instancji przechowują dane specyficzne dla konkretnego obiektu - to dzięki nim każdy obiekt może mieć inny stan. Na przykład obiekt pracownik1 może mieć imię "Anna" i pensję 12000, podczas gdy pracownik2 ma imię "Jan" i pensję 10000.

self to parametr, który odnosi się do konkretnej instancji klasy - Python przekazuje go automatycznie przy wywołaniu metody. Gdy piszesz self.imie = imie , mówisz: "dla tej konkretnej instancji zapisz pod nazwą imie przekazaną wartość".

Atrybuty instancji

Moduł ten stanowi pierwszą część kompleksowego kursu programowania obiektowego w języku Python. Jego celem jest wprowadzenie studentów w paradygmat obiektowy oraz przedstawienie fundamentalnych koncepcji, takich jak klasy, obiekty, atrybuty i metody. Materiał został zaprojektowany tak, aby stopniowo budować zrozumienie od prostych analogii do konkretnych implementacji w kodzie. Każdy slajd zawiera przykłady, które można samodzielnie przetestować w środowisku REPL. Zaleca się aktywne uczestnictwo poprzez modyfikowanie przykładów i wykonywanie ćwiczeń. Systematyczna praca z materiałem gwarantuje solidne opanowanie podstaw OOP. W kolejnych modułach wiedza ta będzie rozwijana o bardziej zaawansowane zagadnienia.

Programowanie obiektowe to nie tylko zestaw reguł składniowych, ale przede wszystkim sposób myślenia o problemach programistycznych. Kluczowe jest zrozumienie, że klasy służą do modelowania rzeczywistych bytów i relacji między nimi. Dzięki OOP kod staje się bardziej modularny, łatwiejszy w utrzymaniu i bardziej odporny na błędy. Współczesne aplikacje webowe, systemy bazodanowe i frameworki w dużym stopniu opierają się na paradygmacie obiektowym. Opanowanie OOP otwiera drzwi do zrozumienia zaawansowanych wzorców projektowych. Zachęcamy do cierpliwej i systematycznej nauki - każde nowe pojęcie będzie szczegółowo wyjaśnione i zilustrowane przykładami.

4 / 50
Kod: definiowanie atrybutów instancji
class Pracownik:
    def __init__(self, imie, stanowisko, pensja):
        self.imie = imie
        self.stanowisko = stanowisko
        self.pensja = pensja
        self.aktywny = True  # wartość domyślna, nieparametr

# Tworzenie obiektów z różnymi danymi
p1 = Pracownik("Anna", "Programista", 12000)
p2 = Pracownik("Jan", "Analityk", 10000)

print(p1.imie)   # Anna
print(p2.pensja) # 10000

# Każdy obiekt ma INNĄ wartość atrybutu instancji
print(p1.stanowisko)  # Programista
print(p2.stanowisko)  # Analityk

# Wszystkie obiekty mają wartość domyślną aktywny = True
print(p1.aktywny)  # True
print(p2.aktywny)  # True
Kod instancji

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

5 / 50
Każdy obiekt ma kopię atrybutów instancji

Niezależność obiektów

Gdy tworzymy dwa obiekty tej samej klasy, każdy z nich ma własne, niezależne atrybuty instancji. Zmiana wartości w jednym obiekcie nie wpływa na drugi - to kluczowa cecha programowania obiektowego, która pozwala modelować rzeczywiste byty jako osobne jednostki.

W praktyce oznacza to, że możesz mieć setki obiektów tej samej klasy, z których każdy przechowuje inne dane. Modyfikacja jednego nie powoduje skutków ubocznych w pozostałych.

p1 = Pracownik("Anna", "Programista", 12000)
p2 = Pracownik("Jan", "Analityk", 10000)

p1.pensja = 14000  # zmiana tylko w p1
print(p2.pensja)     # wciąż 10000
Kopie atrybutów

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

6 / 50
Odczyt i zapis atrybutów instancji

Swobodny dostęp do atrybutów

Atrybuty instancji można odczytywać i modyfikować bezpośrednio z poziomu obiektu za pomocą notacji kropkowej.

# Odczyt
print(p1.imie)
print(p1.pensja)

# Modyfikacja
p1.pensja = 15000
p1.stanowisko = "Senior Programista"

# Dodanie nowego atrybutu (dynamicznie)
p1.dodatkowy_projekt = True

Python pozwala dodawać nowe atrybuty do obiektu również poza klasą - to cecha dynamiczna języka. Nie musisz definiować wszystkich atrybutów w __init__ , choć jest to dobra praktyka, ponieważ ułatwia czytanie kodu i zapobiega błędom. Atrybuty dodane dynamicznie istnieją tylko dla konkretnego obiektu - inne instancje tej samej klasy ich nie mają.

Przykładowo: p1.dodatkowy_projekt = True tworzy atrybut dodatkowy_projekt TYLKO dla obiektu p1 . Obiekt p2 nie będzie miał tego atrybutu, chyba że ręcznie go dodasz. Ta elastyczność jest potężna, ale wymaga dyscypliny - w dużych projektach lepiej definiować wszystkie atrybuty w __init__ .

Odczyt zapis

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

7 / 50
Podsumowanie atrybutów instancji

Najważniejsze fakty

  • Definiowane w __init__ za pomocą self - to standardowy i rekomendowany sposób
  • Każdy obiekt ma własny, niezależny zestaw atrybutów instancji
  • Dostęp przez notację kropkową:obiekt.atrybut (czytaj i pisz)
  • Można je dowolnie modyfikować w trakcie życia obiektu
  • Nowe atrybuty można dodawać dynamicznie (ale ostrożnie)
  • Służą do przechowywania stanu konkretnego obiektu
  • Stan każdego obiektu jest izolowany - nie ma skutków ubocznych między obiektami
  • Atrybuty instancji "przesłaniają" atrybuty klasowe o tej samej nazwie

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

8 / 50
Atrybuty klasowe - wprowadzenie

Definiowane poza metodami

Atrybuty klasowe są definiowane bezpośrednio w ciele klasy, poza jakąkolwiek metodą. Należą do klasy jako całości, a nie do pojedynczego obiektu. W pamięci istnieją w jednej kopii, wspólnej dla wszystkich instancji.

Są współdzielone przez wszystkie instancje danej klasy. Przydają się do przechowywania stałych (np. przeliczników, progów podatkowych), domyślnych wartości, liczników obiektów, konfiguracji wspólnej dla wszystkich instancji.

Nie używamy self - atrybut klasowy jest przypisany bezpośrednio do nazwy klasy. Składnia: nazwa_atrybutu = wartość na poziomie wcięcia klasy.

Typowe zastosowania: stałe matematyczne ( PI = 3.14159 ), wspólne liczniki, mapowanie wartości, konfiguracja domyślna, tabele przeliczeniowe.

Atrybuty klasowe

Moduł ten stanowi pierwszą część kompleksowego kursu programowania obiektowego w języku Python. Jego celem jest wprowadzenie studentów w paradygmat obiektowy oraz przedstawienie fundamentalnych koncepcji, takich jak klasy, obiekty, atrybuty i metody. Materiał został zaprojektowany tak, aby stopniowo budować zrozumienie od prostych analogii do konkretnych implementacji w kodzie. Każdy slajd zawiera przykłady, które można samodzielnie przetestować w środowisku REPL. Zaleca się aktywne uczestnictwo poprzez modyfikowanie przykładów i wykonywanie ćwiczeń. Systematyczna praca z materiałem gwarantuje solidne opanowanie podstaw OOP. W kolejnych modułach wiedza ta będzie rozwijana o bardziej zaawansowane zagadnienia.

Programowanie obiektowe to nie tylko zestaw reguł składniowych, ale przede wszystkim sposób myślenia o problemach programistycznych. Kluczowe jest zrozumienie, że klasy służą do modelowania rzeczywistych bytów i relacji między nimi. Dzięki OOP kod staje się bardziej modularny, łatwiejszy w utrzymaniu i bardziej odporny na błędy. Współczesne aplikacje webowe, systemy bazodanowe i frameworki w dużym stopniu opierają się na paradygmacie obiektowym. Opanowanie OOP otwiera drzwi do zrozumienia zaawansowanych wzorców projektowych. Zachęcamy do cierpliwej i systematycznej nauki - każde nowe pojęcie będzie szczegółowo wyjaśnione i zilustrowane przykładami.

9 / 50
Kod: atrybuty klasowe vs instancyjne
class Pracownik:
    # atrybut klasowy - wspólny dla wszystkich instancji
    podwyzka = 1.05
    limit_urlopu = 26

    def __init__(self, imie, pensja):
        # atrybuty instancji - każdy obiekt ma swoje
        self.imie = imie
        self.pensja = pensja

# Dostęp przez klasę (zalecany dla atrybutów klasowych)
print(Pracownik.podwyzka)      # 1.05
print(Pracownik.limit_urlopu) # 26

# Dostęp przez obiekt (Python szuka najpierw w instancji, potem w klasie)
p = Pracownik("Ola", 8000)
print(p.podwyzka)              # 1.05 - odczytane z klasy
print(p.imie)                 # Ola - atrybut instancji

# Różnica: atrybut klasowy NIE pojawi się w __dict__ obiektu
print(p.__dict__)              # {'imie': 'Ola', 'pensja': 8000}
print(Pracownik.__dict__)      # zawiera 'podwyzka', 'limit_urlopu'

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

10 / 50
Współdzielenie przez wszystkie obiekty

Jeden atrybut klasowy, wiele obiektów

Wszystkie instancje klasy współdzielą tę samą wartość atrybutu klasowego. Jeśli zmienisz go przez klasę, zmiana widoczna jest natychmiast dla wszystkich obiektów - zarówno już istniejących, jak i tych utworzonych w przyszłości.

To potężny mechanizm: możesz zmienić działanie wszystkich obiektów w jednym miejscu. Używa się go do globalnych ustawień, progów podatkowych, stałych konwersji, które mogą się zmieniać w czasie.

p1 = Pracownik("A", 5000)
p2 = Pracownik("B", 6000)

# Wspólna wartość
print(p1.podwyzka)  # 1.05
print(p2.podwyzka)  # 1.05

# Modyfikacja przez klasę
Pracownik.podwyzka = 1.10
print(p1.podwyzka)  # 1.10
print(p2.podwyzka)  # 1.10
Współdzielenie

Tworzenie obiektu przez wywołanie klasy to jeden z najważniejszych wzorców w Pythonie. Składnia NazwaKlasy() może być myląca dla początkujących, ponieważ wygląda jak wywołanie funkcji, ale w rzeczywistości uruchamia złożony proces alokacji i inicjalizacji. Python, jako język dynamiczny, pozwala na tworzenie obiektów w dowolnym momencie działania programu, w tym wewnątrz pętli, warunków czy wyrażeń listowych. Każde wywołanie klasy tworzy nowy, niezależny obiekt w pamięci, nawet jeżeli klasa nie ma zdefiniowanego konstruktora. Zmienna przechowuje referencję do obiektu, a nie sam obiekt, co ma kluczowe znaczenie dla zrozumienia mechanizmu przypisań i kopiowania.

Proces tworzenia obiektu składa się z kilku etapów: najpierw wywoływana jest metoda __new__, która alokuje pamięć dla nowej instancji, następnie uruchamiany jest __init__ do inicjalizacji atrybutów, a na końcu zwracana jest referencja do gotowego obiektu. Większość programistów modyfikuje wyłącznie __init__, pozostawiając __new__ w domyślnej implementacji. Zrozumienie tej kolejności jest ważne przy tworzeniu bardziej zaawansowanych wzorców, takich jak Singleton czy Fabryka. Gdy tworzymy wiele obiektów w pętli, każdy z nich jest niezależną instancją z własnym stanem i tożsamością.

11 / 50
Dostęp przez klasę i przez obiekt

Dwie drogi dostępu

Do atrybutu klasowego można się odwołać na dwa sposoby - ważne jest, aby rozumieć różnicę:

# 1. Poprzez klasę (zalecane - jednoznaczne i czytelne)
Pracownik.podwyzka

# 2. Poprzez instancję (Python szuka najpierw w instancji, potem w klasie)
p = Pracownik("Kasia", 7000)
p.podwyzka

# Sprawdźmy, czy to fizycznie ten sam obiekt w pamięci
print(p.podwyzka is Pracownik.podwyzka)  # True - to ta sama wartość

# Ale uwaga! Przypisanie przez instancję zmienia zachowanie
p.podwyzka = 1.15  # to TWOŻY nowy atrybut instancji!
print(p.podwyzka is Pracownik.podwyzka)  # False - już nie to samo

Jeśli obiekt nie ma własnego atrybutu o danej nazwie, Python zagląda do __dict__ klasy. To działa jak dziedziczenie - obiekt "dziedziczy" wartość z klasy, ale może ją przesłonić własnym atrybutem.

Dostęp klasa obiekt

Tworzenie obiektu przez wywołanie klasy to jeden z najważniejszych wzorców w Pythonie. Składnia NazwaKlasy() może być myląca dla początkujących, ponieważ wygląda jak wywołanie funkcji, ale w rzeczywistości uruchamia złożony proces alokacji i inicjalizacji. Python, jako język dynamiczny, pozwala na tworzenie obiektów w dowolnym momencie działania programu, w tym wewnątrz pętli, warunków czy wyrażeń listowych. Każde wywołanie klasy tworzy nowy, niezależny obiekt w pamięci, nawet jeżeli klasa nie ma zdefiniowanego konstruktora. Zmienna przechowuje referencję do obiektu, a nie sam obiekt, co ma kluczowe znaczenie dla zrozumienia mechanizmu przypisań i kopiowania.

Proces tworzenia obiektu składa się z kilku etapów: najpierw wywoływana jest metoda __new__, która alokuje pamięć dla nowej instancji, następnie uruchamiany jest __init__ do inicjalizacji atrybutów, a na końcu zwracana jest referencja do gotowego obiektu. Większość programistów modyfikuje wyłącznie __init__, pozostawiając __new__ w domyślnej implementacji. Zrozumienie tej kolejności jest ważne przy tworzeniu bardziej zaawansowanych wzorców, takich jak Singleton czy Fabryka. Gdy tworzymy wiele obiektów w pętli, każdy z nich jest niezależną instancją z własnym stanem i tożsamością.

12 / 50
Podsumowanie atrybutów klasowych

Najważniejsze fakty

  • Definiowane bezpośrednio w ciele klasy, poza metodami - bez self
  • Współdzielone przez wszystkie instancje - jedna kopia w pamięci
  • Dostęp przez Klasa.atrybut (zalecane) lub obiekt.atrybut
  • Zmiana przez klasę wpływa na wszystkie obiekty - natychmiast i globalnie
  • Przydatne do stałych, liczników, konfiguracji, tabel przeliczeniowych
  • Mogą być przesłonięte (shadowing) przez atrybut instancji o tej samej nazwie
  • Przypisanie przez instancję: obiekt.atrybut = X NIE modyfikuje atrybutu klasowego - tworzy nowy atrybut instancji
  • Atrybuty klasowe są widoczne w Klasa.__dict__, a NIE w obiekt.__dict__
Podsumowanie klasowe

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

13 / 50
Diagram porównawczy instancja vs klasa

Różnice w pamięci

# Klasa w pamięci - istnieje w jednej kopii
Klasa Pracownik
 +-- podwyzka: 1.05 (atrybut klasowy - wspólny)
 +-- limit_urlopu: 26 (atrybut klasowy)
 +-- __init__: <function> (metoda)

# Obiekt p1 - ma TYLKO atrybuty instancji
p1 (Pracownik)
 +-- imie: "Anna" (instancji)
 +-- pensja: 12000 (instancji)
 +-- aktywny: True (instancji)
 +--> podwyzka: 1.05 (ODWOŁANIE do klasy - nie kopia!)

# Obiekt p2 - niezależny zestaw atrybutów instancji
p2 (Pracownik)
 +-- imie: "Jan" (instancji)
 +-- pensja: 10000 (instancji)
 +-- aktywny: True (instancji)
 +--> podwyzka: 1.05 (ODWOŁANIE do klasy)

# Kluczowa obserwacja: atrybut 'podwyzka' NIE jest kopiowany
# do każdego obiektu - Python przechowuje go raz w klasie,
# a obiekty "widzą" go przez mechanizm wyszukiwania atrybutów.
Diagram

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

14 / 50
Kod: zmiana atrybutu klasowego przez instancję
class Pracownik:
    podwyzka = 1.05

    def __init__(self, imie):
        self.imie = imie

p = Pracownik("Ewa")
print(p.podwyzka)        # 1.05 (odczytane z klasy)

# Uwaga! To NIE modyfikuje atrybutu klasowego!
# Python tworzy NOWY atrybut instancji 'podwyzka'
p.podwyzka = 1.15

print(p.podwyzka)        # 1.15 (własny atrybut instancji)
print(Pracownik.podwyzka) # 1.05 (klasowy bez zmian - nietknięty)

# Sprawdźmy __dict__ - teraz p ma własny 'podwyzka'
print(p.__dict__)        # {'imie': 'Ewa', 'podwyzka': 1.15}
# Przed przypisaniem: {'imie': 'Ewa'}

To kluczowa koncepcja: przypisanie przez instancję ZAWSZE tworzy lub modyfikuje atrybut instancji , nigdy nie modyfikuje atrybutu klasowego. Gdy piszesz obiekt.atrybut = X , Python działa na __dict__ obiektu, a nie klasy.

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

15 / 50
Kod: zmiana atrybutu klasowego przez klasę
class Pracownik:
    podwyzka = 1.05

    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja

p1 = Pracownik("A", 5000)
p2 = Pracownik("B", 6000)

# Zmiana przez KLASĘ - to jest poprawny sposób
Pracownik.podwyzka = 1.10

# Wszystkie obiekty natychmiast widzą zmianę
print(p1.podwyzka)  # 1.10
print(p2.podwyzka)  # 1.10
print(Pracownik.podwyzka) # 1.10 - bezpośrednio

# Również nowo utworzone obiekty zobaczą zmienioną wartość
p3 = Pracownik("C", 7000)
print(p3.podwyzka)  # 1.10 - odziedziczona po klasie
Zmiana przez klasę

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

16 / 50
Kod: efekt cienia (shadowing)
class Pracownik:
    podwyzka = 1.05

    def __init__(self, imie):
        self.imie = imie

p1 = Pracownik("X")
p2 = Pracownik("Y")

p1.podwyzka = 1.20   # shadowing: p1 dostaje własny atrybut instancji

print(p1.podwyzka)        # 1.20 - atrybut instancji (przesłania klasowy)
print(p2.podwyzka)        # 1.05 - wciąż odczyt z klasy
print(Pracownik.podwyzka) # 1.05 - klasowy bez zmian

# p1 ma teraz dwa różne atrybuty 'podwyzka'?
# Nie - p1.podwyzka to atrybut instancji, który przesłania atrybut klasowy o tej samej nazwie.
# Python przy wyszukiwaniu zagląda najpierw do obiektu,
# potem do klasy. Znajduje w obiekcie - zwraca go.

# Potwierdzenie przez __dict__
print(p1.__dict__)  # {'imie': 'X', 'podwyzka': 1.20}
print(p2.__dict__)  # {'imie': 'Y'} - brak 'podwyzka'
Shadowing

Slajd zatytułowany "Kod: efekt cienia (shadowing)" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

17 / 50
Tabela porównawcza

Atrybut instancji vs klasowy

+-----------------------------------+-----------------------------------+
| Atrybut instancji                 | Atrybut klasowy                   |
+-----------------------------------+-----------------------------------+
| Definiowany w __init__ przez self | Definiowany w ciele klasy bez self|
+-----------------------------------+-----------------------------------+
| Każdy obiekt ma własną kopię     | Współdzielony przez wszystkie obiekty|
+-----------------------------------+-----------------------------------+
| Modyfikacja tylko danego obiektu  | Modyfikacja wpływa na wszystkie  |
+-----------------------------------+-----------------------------------+
| self.nazwa = wartość             | Klasa.nazwa = wartość          |
+-----------------------------------+-----------------------------------+
| Istnieje w obiekt.__dict__        | Istnieje w Klasa.__dict__         |
+-----------------------------------+-----------------------------------+
| Tworzony przy każdym obiekcie   | Tworzony raz przy definicji klasy |
+-----------------------------------+-----------------------------------+
| Można dodawać dynamicznie       | Stały dla wszystkich instancji    |
+-----------------------------------+-----------------------------------+
| Przykład: self.imie = "Anna"    | Przykład: Pracownik.podwyzka=1.05|
+-----------------------------------+-----------------------------------+
Tabela

Slajd zatytułowany "Tabela porównawcza" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

18 / 50
Metody instancji - wprowadzenie

Odczyt i modyfikacja zawsze self (odniesienie

Metody instancji to funkcje zdefiniowane wewnątrz klasy, które jako pierwszy parametr przyjmują self . Dzięki self mają dostęp do atrybutów danego obiektu - to jest główny mechanizm interakcji z danymi w OOP.

Metody instancji mogą:

  • odczytywać stan obiektu (self.atrybut) - np. wyświetlić imię pracownika
  • modyfikować stan obiektu (self.atrybut = nowa_wartość) - np. zmienić pensję
  • wywoływać inne metody (self.inna_metoda()) - np. awans wywołuje zmianę stanowiska i podwyżkę
  • przyjmować dodatkowe parametry (jak zwykłe funkcje) - np.daj_podwyzke(self, procent)
  • zwracać wartości (return) - np. obliczoną pensję roczną

Pierwszy parametr self jest przekazywany automatycznie przez Pythona - nie podajesz go przy wywołaniu. Gdy piszesz obiekt.metoda(arg) , Python przekształca to na Klasa.metoda(obiekt, arg) .

Metody instancji

Moduł ten stanowi pierwszą część kompleksowego kursu programowania obiektowego w języku Python. Jego celem jest wprowadzenie studentów w paradygmat obiektowy oraz przedstawienie fundamentalnych koncepcji, takich jak klasy, obiekty, atrybuty i metody. Materiał został zaprojektowany tak, aby stopniowo budować zrozumienie od prostych analogii do konkretnych implementacji w kodzie. Każdy slajd zawiera przykłady, które można samodzielnie przetestować w środowisku REPL. Zaleca się aktywne uczestnictwo poprzez modyfikowanie przykładów i wykonywanie ćwiczeń. Systematyczna praca z materiałem gwarantuje solidne opanowanie podstaw OOP. W kolejnych modułach wiedza ta będzie rozwijana o bardziej zaawansowane zagadnienia.

Programowanie obiektowe to nie tylko zestaw reguł składniowych, ale przede wszystkim sposób myślenia o problemach programistycznych. Kluczowe jest zrozumienie, że klasy służą do modelowania rzeczywistych bytów i relacji między nimi. Dzięki OOP kod staje się bardziej modularny, łatwiejszy w utrzymaniu i bardziej odporny na błędy. Współczesne aplikacje webowe, systemy bazodanowe i frameworki w dużym stopniu opierają się na paradygmacie obiektowym. Opanowanie OOP otwiera drzwi do zrozumienia zaawansowanych wzorców projektowych. Zachęcamy do cierpliwej i systematycznej nauki - każde nowe pojęcie będzie szczegółowo wyjaśnione i zilustrowane przykładami.

19 / 50
Kod: metody odczytujące atrybuty
class Pracownik:
    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja

    def przedstaw_sie(self):
        """Zwraca napis z imieniem pracownika."""
        return f"Mam na imię {self.imie}"

    def pensja_roczna(self):
        """Oblicza roczne wynagrodzenie (pensja * 12)."""
        return self.pensja * 12

    def podaj_stawke_godzinowa(self, godzin_w_miesiacu=160):
        """Oblicza stawkę godzinową przy domyślnym wymiarze godzin."""
        return self.pensja / godzin_w_miesiacu

p = Pracownik("Tomek", 9000)
print(p.przedstaw_sie())              # Mam na imię Tomek
print(p.pensja_roczna())              # 108000
print(p.podaj_stawke_godzinowa())     # 56.25
print(p.podaj_stawke_godzinowa(168))  # 53.57 - przy innym wymiarze
Metody odczyt

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

20 / 50
Kod: metody modyfikujące atrybuty
class Pracownik:
    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja
        self.stanowisko = "Praktykant"  # wartość domyślna

    def daj_podwyzke(self, procent):
        """Zwiększa pensję o podany procent (np. 10 = 10%)."""
        if procent < 0:
            print("Procent nie może być ujemny!")
            return
        self.pensja *= (1 + procent / 100)
        print(f"Nowa pensja: {self.pensja:.2f}")

    def zmien_stanowisko(self, nowe_stanowisko):
        """Ustawia nowe stanowisko dla pracownika."""
        stary = self.stanowisko
        self.stanowisko = nowe_stanowisko
        print(f"{self.imie}: {stary} -> {nowe_stanowisko}")

p = Pracownik("Marek", 10000)
p.zmien_stanowisko("Młodszy Programista")
p.daj_podwyzke(10)                # podwyżka o 10%
print(p.pensja)                    # 11000.0
p.daj_podwyzke(-5)                # obsługa błędu
Metody modyfikacja

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

21 / 50
Metody wywołujące inne metody
class Pracownik:
    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja
        self.stanowisko = "Praktykant"

    def daj_podwyzke(self, procent):
        self.pensja *= (1 + procent / 100)

    def zmien_stanowisko(self, nowe):
        self.stanowisko = nowe

    def awans(self, nowe_stanowisko, podwyzka_proc):
        """Połączona operacja: zmiana stanowiska + podwyżka.
        To jest przykład metody, która wywołuje inne metody.
        """
        self.zmien_stanowisko(nowe_stanowisko)
        self.daj_podwyzke(podwyzka_proc)
        print(f"Gratulacje, {self.imie} awansował na {nowe_stanowisko}!")

    def podsumowanie(self):
        """Wyświetla pełne podsumowanie stanu obiektu."""
        print(f"Pracownik: {self.imie}")
        print(f"Stanowisko: {self.stanowisko}")
        print(f"Miesięczna pensja: {self.pensja:.2f}")

p = Pracownik("Ola", 8000)
p.podsumowanie()
# Pracownik: Ola
# Stanowisko: Praktykant
# Miesięczna pensja: 8000.00
p.awans("Dyrektor", 30)
p.podsumowanie()
# Pracownik: Ola
# Stanowisko: Dyrektor
# Miesięczna pensja: 10400.00
Metody wywołujące

Metody są funkcjami zdefiniowanymi w przestrzeni nazw klasy, które otrzymują automatycznie pierwszy argument w postaci referencji do obiektu. Dzięki temu mogą odczytywać i modyfikować stan obiektu, na którym zostały wywołane. W Pythonie istnieje kilka rodzajów metod: instancyjne (z self), klasowe (z cls i dekoratorem @classmethod), statyczne (z @staticmethod) oraz abstrakcyjne (z @abstractmethod w klasach ABC). Metody instancyjne są najczęściej używane i to one będą głównym tematem tej części kursu. Każda metoda instancyjna przyjmuje self jako pierwszy parametr, a Python automatycznie przekazuje obiekt w momencie wywołania przez notację kropkową. Metody mogą przyjmować dodatkowe argumenty, mieć wartości domyślne i zwracać wartości za pomocą instrukcji return.

Dobrą praktyką projektowania metod jest zasada pojedynczej odpowiedzialności - każda metoda powinna wykonywać jedną, dobrze określoną operację. Metody tylko do odczytu stanu, nazywane getterami, nie powinny modyfikować atrybutów obiektu. Metody modyfikujące stan, nazywane setterami, powinny walidować dane wejściowe przed wprowadzeniem zmian. Przestrzeganie tych zasad prowadzi do kodu łatwiejszego w testowaniu i debugowaniu. Warto również stosować metody pomocnicze, aby uniknąć powielania kodu wewnątrz klasy. Dobrze zaprojektowane metody sprawiają, że klasa jest intuicyjna w użyciu i trudna do niepoprawnego wykorzystania.

22 / 50
Podsumowanie metod instancji

Najważniejsze fakty

  • Pierwszy parametr to zawsze self (odniesienie do konkretnej instancji)
  • Mogą odczytywać i modyfikować atrybuty instancji przez self
  • Mogą wywoływać inne metody za pomocą self.metoda()
  • Wywołujemy je na obiekcie:obiekt.metoda() - Python automatycznie podaje self
  • Mogą przyjmować argumenty (jak zwykłe funkcje) i zwracać wartości
  • Stanowią interfejs do kontrolowanej manipulacji stanem obiektu
  • Dobrze zaprojektowane metody ukrywają szczegóły implementacji (enkapsulacja)
  • Metody mogą mieć dokumentację (docstring) - opisującą, co robią
Podsumowanie metod

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

23 / 50
Modyfikacja atrybutów klasowych - ważna różnica

Przez klasę vs przez instancję

To jeden z najczęstszych powodów błędów u początkujących - a także u doświadczonych programistów przechodzących z innych języków. Zapamiętaj tę kluczową różnicę:

  • Klasa.atrybut = X → modyfikuje atrybut klasowy (wpływa na wszystkie obiekty, również te utworzone w przyszłości)
  • obiekt.atrybut = X → tworzy NOWY atrybut instancji o nazwie 'atrybut' (nie modyfikuje klasowego!)
  • obiekt.atrybut(odczyt) → najpierw szuka w instancji, potem w klasie

Ta różnica wynika z mechanizmu wyszukiwania atrybutów w Pythonie (tzw. descriptor protocol i MRO): najpierw sprawdzany jest __dict__ obiektu, potem __dict__ klasy i ewentualnie klas nadrzędnych. Przypisanie zawsze działa na __dict__ obiektu, nigdy klasy.

W innych językach (Java, C++) przypisanie na obiekcie może modyfikować pole klasowe - w Pythonie to nie działa w ten sposób.

Różnica modyfikacji

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

24 / 50
Kod: modyfikacja przez klasę
class Pracownik:
    limit_urlopu = 26

    def __init__(self, imie):
        self.imie = imie

# Modyfikacja przez klasę - działa globalnie dla wszystkich
Pracownik.limit_urlopu = 20

p1 = Pracownik("A")
p2 = Pracownik("B")

# Wszystkie obiekty widzą nową wartość
print(p1.limit_urlopu)  # 20
print(p2.limit_urlopu)  # 20
print(Pracownik.limit_urlopu) # 20

# Uwaga: kolejność nie ma znaczenia - zmiana przez klasę
# wpływa na wszystkie obiekty, zarówno przed jak i po zmianie

Slajd zatytułowany "Kod: modyfikacja przez klasę" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

25 / 50
Kod: modyfikacja przez instancję (uwaga!)
class Pracownik:
    limit_urlopu = 26

    def __init__(self, imie):
        self.imie = imie

p1 = Pracownik("A")
p2 = Pracownik("B")

# UWAGA! To NIE modyfikuje atrybutu klasowego!
# Python tworzy NOWY atrybut instancji 'limit_urlopu' TYLKO dla p1
p1.limit_urlopu = 30

print(p1.limit_urlopu)  # 30 (własny atrybut instancji p1)
print(p2.limit_urlopu)  # 26 (nietknięty, wciąż z klasy)
print(Pracownik.limit_urlopu) # 26 (klasowy bez zmian)

# Dowód: __dict__ p1 zawiera teraz 'limit_urlopu'
print(p1.__dict__)  # {'imie': 'A', 'limit_urlopu': 30}
print(p2.__dict__)  # {'imie': 'B'} - brak 'limit_urlopu'

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

26 / 50
Kod: kiedy to ma znaczenie
class Konfiguracja:
    wersja = "1.0"
    debug = False

    def __init__(self, nazwa):
        self.nazwa = nazwa

cfg1 = Konfiguracja("aplikacja")
cfg2 = Konfiguracja("test")

# Włączenie debugu GLOBALNIE przez klasę - DZIAŁA
Konfiguracja.debug = True
print(cfg1.debug)  # True - widoczne w obu
print(cfg2.debug)  # True

# Próba włączenia TYLKO dla jednego - UWAGA!
cfg2.debug = False      # tworzy NOWY atrybut instancji w cfg2

print(cfg1.debug)  # True (wciąż z klasy)
print(cfg2.debug)  # False (własny atrybut instancji)
print(Konfiguracja.debug)  # True (klasowy nietknięty)

# To częsty błąd: programista myśli, że zmienia wartość
# globalną, a tak naprawdę tworzy atrybut instancji.
# Zawsze modyfikuj atrybuty klasowe przez klasę!
Kiedy znaczenie

Slajd zatytułowany "Kod: kiedy to ma znaczenie" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

27 / 50
Zasada: klasowe przez klasę

Złota zasada

Atrybuty klasowe modyfikuj zawsze przez klasę, nigdy przez instancję.

Jeśli chcesz zmienić wartość współdzieloną przez wszystkie obiekty, użyj:

# Poprawnie - zmienia wartość dla wszystkich
Klasa.atrybut_klasowy = nowa_wartosc

# Błędnie - tworzy NOWY atrybut instancji
obiekt.atrybut_klasowy = nowa_wartosc

Wyjątkiem jest celowe shadowing, gdy świadomie chcesz, aby jeden konkretny obiekt miał inną wartość niż reszta. W takim przypadku warto dodać komentarz wyjaśniający intencję, aby inny programista nie uznał tego za błąd.

Pamiętaj: Klasa.atrybut = do zapisu, Klasa.atrybut do odczytu - jeśli chcesz mieć pewność, że pracujesz na wartości wspólnej.

Zasada

Slajd zatytułowany "Zasada: klasowe przez klasę" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

28 / 50
__dict__- słownik atrybutów

Wprowadzenie

Każdy obiekt w Pythonie ma specjalny atrybut __dict__ , który przechowuje słownik wszystkich atrybutów instancji. Podobnie klasa ma swój własny __dict__ z atrybutami klasowymi i metodami. To właśnie ten mechanizm umożliwia Pythonowi szybkie wyszukiwanie atrybutów.

__dict__ pozwala podejrzeć, jakie atrybuty posiada obiekt lub klasa w danym momencie - to potężne narzędzie do debugowania i introspekcji.

Jest to mechanizm używany wewnętrznie przez Python przy wyszukiwaniu atrybutów: najpierw sprawdzany jest __dict__ obiektu, a jeśli atrybut nie zostanie znaleziony, sprawdzany jest __dict__ klasy (i klas nadrzędnych).

Należy pamiętać, że nie wszystkie obiekty mają __dict__ - klasy korzystające z __slots__ nie mają tego atrybutu, co oszczędza pamięć.

dict

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

29 / 50
Kod:obiekt.__dict__
class Pracownik:
    podwyzka = 1.05

    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja

p = Pracownik("Ela", 7000)
print(p.__dict__)
# {'imie': 'Ela', 'pensja': 7000}

# Atrybut klasowy NIE jest w __dict__ obiektu!
# 'podwyzka' jest w Pracownik.__dict__, a nie w p.__dict__

# Możemy ręcznie sprawdzić różnicę:
print("imie" in p.__dict__)             # True - instancji
print("podwyzka" in p.__dict__)         # False - klasowy
print("podwyzka" in Pracownik.__dict__)  # True
Obiekt dict

Tworzenie obiektu przez wywołanie klasy to jeden z najważniejszych wzorców w Pythonie. Składnia NazwaKlasy() może być myląca dla początkujących, ponieważ wygląda jak wywołanie funkcji, ale w rzeczywistości uruchamia złożony proces alokacji i inicjalizacji. Python, jako język dynamiczny, pozwala na tworzenie obiektów w dowolnym momencie działania programu, w tym wewnątrz pętli, warunków czy wyrażeń listowych. Każde wywołanie klasy tworzy nowy, niezależny obiekt w pamięci, nawet jeżeli klasa nie ma zdefiniowanego konstruktora. Zmienna przechowuje referencję do obiektu, a nie sam obiekt, co ma kluczowe znaczenie dla zrozumienia mechanizmu przypisań i kopiowania.

Proces tworzenia obiektu składa się z kilku etapów: najpierw wywoływana jest metoda __new__, która alokuje pamięć dla nowej instancji, następnie uruchamiany jest __init__ do inicjalizacji atrybutów, a na końcu zwracana jest referencja do gotowego obiektu. Większość programistów modyfikuje wyłącznie __init__, pozostawiając __new__ w domyślnej implementacji. Zrozumienie tej kolejności jest ważne przy tworzeniu bardziej zaawansowanych wzorców, takich jak Singleton czy Fabryka. Gdy tworzymy wiele obiektów w pętli, każdy z nich jest niezależną instancją z własnym stanem i tożsamością.

30 / 50
Kod:Klasa.__dict__
class Pracownik:
    podwyzka = 1.05

    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja

    def przedstaw_sie(self):
        return f"Jestem {self.imie}"

# __dict__ klasy zawiera metody i atrybuty klasowe
print(Pracownik.__dict__)
# {'__module__': '__main__', 'podwyzka': 1.05,
#  '__init__': <function>, 'przedstaw_sie': <function>, ...}

# Kluczowa obserwacja: metody to też atrybuty klasy!
# Są one funkcjami zapisanymi w __dict__ klasy.

# Można też podejrzeć tylko własne atrybuty klasy:
for nazwa, wartosc in Pracownik.__dict__.items():
    if not nazwa.startswith("__"):  # pomiń atrybuty specjalne
        print(nazwa, wartosc)
# podwyzka 1.05
# __init__ <function>
# przedstaw_sie <function>
Klasa dict

Slajd zatytułowany "Kod: Klasa.__dict__" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

31 / 50
Kod: modyfikacja przez __dict__
class Pracownik:
    def __init__(self, imie):
        self.imie = imie

p = Pracownik("Iza")

# Modyfikacja przez __dict__
p.__dict__["imie"] = "Izabela"
print(p.imie)  # Izabela

# Dodanie nowego atrybutu przez __dict__
p.__dict__["wiek"] = 30
print(p.wiek)  # 30

# To samo co: p.wiek = 30
Modyfikacja dict

Slajd zatytułowany "Kod: modyfikacja przez __dict__" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

32 / 50
Kiedy używać __dict__

Zastosowania __dict__

  • Debugowanie - podgląd wszystkich atrybutów obiektu w jednym miejscu
  • Introspekcja - sprawdzenie, jakie atrybuty posiada obiekt w danym momencie
  • Serializacja - zamiana obiektu na słownik (np. do JSON), dzięki obj.__dict__
  • Dynamiczne tworzenie atrybutów -obj.__dict__["nowy"] = wartosc
  • Kopiowanie atrybutów między obiektami -obj2.__dict__.update(obj1.__dict__)
  • Testy jednostkowe - sprawdzenie, czy obiekt ma oczekiwane atrybuty

Uwaga: Normalnie używaj notacji kropkowej ( obiekt.atrybut ). __dict__ to narzędzie pomocnicze do zaawansowanych zastosowań, nie główny mechanizm dostępu do atrybutów.

Przykład serializacji: json.dumps(p.__dict__) zamieni obiekt na JSON - to wygodny sposób na zapisanie stanu obiektu do pliku lub przesłanie przez sieć.

Kiedy dict

Slajd zatytułowany "Kiedy używać __dict__" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

33 / 50
Usuwanie i sprawdzanie atrybutów

Wprowadzenie

Python oferuje wbudowane funkcje do zarządzania atrybutami, które działają na każdym obiekcie:

  • del obiekt.atrybut - usuwa atrybut z obiektu (usunięcie z __dict__)
  • hasattr(obiekt, "nazwa") - sprawdza, czy atrybut istnieje (zwraca True / False )
  • getattr(obiekt, "nazwa", domyslna) - odczytuje wartość lub zwraca domyślną
  • setattr(obiekt, "nazwa", wartosc) - ustawia wartość atrybutu

Są to funkcje wbudowane, które działają na każdym obiekcie - nie tylko na instancjach klas, ale również na modułach i innych obiektach Pythona. Szczególnie przydatne, gdy nazwa atrybutu jest przechowywana w zmiennej (dostęp dynamiczny).

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

34 / 50
Kod:del- usuwanie atrybutu
class Pracownik:
    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja
        self.tymczasowe = None

p = Pracownik("Bartek", 11000)
print(p.__dict__)
# {'imie': 'Bartek', 'pensja': 11000, 'tymczasowe': None}

# Usunięcie atrybutu instancji za pomocą del
del p.tymczasowe

# Próba dostępu - błąd AttributeError
# print(p.tymczasowe)  # AttributeError: 'Pracownik' object has no attribute 'tymczasowe'

# Sprawdźmy __dict__ po usunięciu
print(p.__dict__)
# {'imie': 'Bartek', 'pensja': 11000}

# Można też usunąć przez del z __dict__:
del p.__dict__["pensja"]
# print(p.pensja)  # AttributeError - usunięte!

# Uwaga: del na atrybucie klasowym usuwa go z klasy,
# ale del na atrybucie instancji usuwa go tylko z tego obiektu.
del

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

35 / 50
Kod: hasattr i getattr
class Pracownik:
    podwyzka = 1.05

    def __init__(self, imie, pensja):
        self.imie = imie
        self.pensja = pensja

    def pensja_roczna(self):
        return self.pensja * 12

p = Pracownik("Michał", 9000)

# hasattr - bezpieczne sprawdzenie bez wyjątku
print(hasattr(p, "imie"))         # True - atrybut instancji
print(hasattr(p, "wiek"))         # False - nie istnieje
print(hasattr(p, "podwyzka"))    # True - atrybut klasowy też widoczny
print(hasattr(p, "pensja_roczna")) # True - metoda to też atrybut

# getattr - odczyt z opcjonalną wartością domyślną
print(getattr(p, "imie"))         # Michał
print(getattr(p, "wiek", "B/D")) # B/D - bo 'wiek' nie istnieje

# getattr bez domyślnej rzuci AttributeError
# print(getattr(p, "wiek"))  # AttributeError!

# Przydatne do dynamicznego dostępu do atrybutów:
nazwa_atrybutu = "pensja"
print(getattr(p, nazwa_atrybutu))  # 9000 - dynamicznie!

# getattr działa też z metodami:
metoda = getattr(p, "pensja_roczna")
print(metoda())  # 108000
hasattr getattr

Slajd zatytułowany "Kod: hasattr i getattr" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

36 / 50
Kod:setattr
class Pracownik:
    def __init__(self, imie):
        self.imie = imie

p = Pracownik("Nina")

# setattr - ustawianie atrybutu (działa jak notacja kropkowa)
setattr(p, "wiek", 28)
setattr(p, "imie", "Nina Kowalska")

print(p.wiek)  # 28
print(p.imie)  # Nina Kowalska

# Kluczowa zaleta: nazwa atrybutu może być dynamiczna
nazwa = "miasto"
setattr(p, nazwa, "Warszawa")
print(p.miasto)  # Warszawa

# Przydatne w pętlach lub przy odczycie danych z pliku:
dane = {"wiek": 30, "miasto": "Kraków", "pensja": 5000}
for klucz, wartosc in dane.items():
    setattr(p, klucz, wartosc)
print(p.miasto)  # Kraków
print(p.pensja)  # 5000

# setattr jest równoważne: p.wiek = 28
# Ale umożliwia ustawianie atrybutów, gdy nazwa jest zmienną
setattr

Slajd zatytułowany "Kod: setattr" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

37 / 50
Podsumowanie funkcji atrybutów

Funkcje wbudowane do zarządzania atrybutami

+---------------------------+----------------------------------+
| Funkcja                   | Działanie                      |
+---------------------------+----------------------------------+
| obiekt.atrybut            | Odczyt (rzuca AttributeError    |
|                           | jeśli nie istnieje)            |
+---------------------------+----------------------------------+
| obiekt.atrybut = X        | Zapis (tworzy lub modyfikuje)   |
+---------------------------+----------------------------------+
| del obiekt.atrybut        | Usuwa atrybut z __dict__        |
+---------------------------+----------------------------------+
| hasattr(obiekt, "nazwa")  | Sprawdza czy istnieje (T/F)     |
+---------------------------+----------------------------------+
| getattr(obiekt, "nazwa")  | Odczytuje (lub AttributeError)  |
+---------------------------+----------------------------------+
| getattr(obiekt, "n", def) | Odczytuje z domyślną          |
+---------------------------+----------------------------------+
| setattr(obiekt, "n", w)   | Ustawia wartość (zapis)      |
+---------------------------+----------------------------------+

Funkcje hasattr , getattr , setattr są szczególnie przydatne, gdy nazwy atrybutów są dynamiczne (pochodzą ze zmiennej, pliku konfiguracyjnego, bazy danych). W codziennym kodzie preferuj notację kropkową - jest czytelniejsza i szybsza.

Podsumowanie funkcji

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

38 / 50
Stan obiektu - zmienność w czasie

Wprowadzenie

Stan obiektu to wartości wszystkich jego atrybutów instancji w danym momencie - innymi słowy, pełna "migawka" danych obiektu. Stan może się zmieniać w czasie życia obiektu poprzez wywoływanie metod, które modyfikują atrybuty.

To odróżnia programowanie obiektowe od funkcyjnego: obiekty mają tożsamość (każdy obiekt jest unikalny, nawet jeśli ma te same wartości) i zmienny stan (może się zmieniać w czasie).

Metody instancji są głównym mechanizmem do zmiany stanu obiektu w kontrolowany sposób. Dobrze zaprojektowana klasa udostępnia metody, które zmieniają stan w przewidywalny i bezpieczny sposób, zapobiegając niepoprawnym stanom (np. ujemne saldo).

Stan obiektu

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

39 / 50
Kod: zmiana stanu przez metody
class KontoBankowe:
    # atrybut klasowy - wspólny dla wszystkich kont
    oprocentowanie = 0.03

    def __init__(self, wlasciciel, saldo=0):
        self.wlasciciel = wlasciciel
        self.saldo = saldo
        self.aktywne = True
        self.historia = []  # lista operacji

    def wplata(self, kwota):
        """Dodaje środki do konta."""
        if kwota <= 0:
            print("Kwota musi być dodatnia!")
            return False
        self.saldo += kwota
        self.historia.append(f"Wpłata: +{kwota}")
        return True

    def wyplata(self, kwota):
        """Wypłaca środki, jeśli saldo jest wystarczające."""
        if not self.aktywne:
            print("Konto jest zamknięte!")
            return False
        if kwota <= 0:
            print("Kwota musi być dodatnia!")
            return False
        if self.saldo >= kwota:
            self.saldo -= kwota
            self.historia.append(f"Wypłata: -{kwota}")
            return True
        print("Niewystarczające środki!")
        return False

    def zamknij(self):
        """Zamyka konto - ustawia aktywne na False."""
        self.aktywne = False
        self.historia.append("Konto zamknięte")

    def pokaz_historie(self):
        """Wyświetla historię operacji na koncie."""
        print(f"Historia konta {self.wlasciciel}:")
        for operacja in self.historia:
            print(f"  - {operacja}")

k = KontoBankowe("Kasia", 1000)
k.wplata(500)
k.wyplata(200)
print(k.saldo)  # 1300
k.pokaz_historie()
# Wpłata: +500
# Wypłata: -200
Zmiana stanu kod

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

40 / 50
Kod: stan przed i po operacji
class KontoBankowe:
    def __init__(self, wlasciciel, saldo=0):
        self.wlasciciel = wlasciciel
        self.saldo = saldo
        self.historia = []

    def wplata(self, kwota):
        self.saldo += kwota
        self.historia.append(f"Wpłata: +{kwota}")
        print(f"Stan po wpłacie: {self.saldo}")

    def pokaz_stan(self):
        """Wyświetla aktualny stan konta."""
        print("=" * 30)
        print(f"Właściciel: {self.wlasciciel}")
        print(f"Saldo: {self.saldo}")
        print(f"Liczba operacji: {len(self.historia)}")
        print("=" * 30)

k = KontoBankowe("Tomek", 2000)
k.pokaz_stan()
# ==============================
# Właściciel: Tomek
# Saldo: 2000
# Liczba operacji: 0
# ==============================

k.wplata(300)
# Stan po wpłacie: 2300

k.pokaz_stan()
# ==============================
# Właściciel: Tomek
# Saldo: 2300
# Liczba operacji: 1
# ==============================
Stan przed po

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

41 / 50
Niezmienność atrybutów klasowych

Atrybuty klasowe jako stałe

Atrybuty klasowe często pełnią rolę stałych (ang. constants), które nie powinny się zmieniać - np. przeliczniki jednostek, progi podatkowe, limity, wartości konfiguracyjne. Dobrą praktyką jest zapisywanie ich WIELKIMI LITERAMI, aby sygnalizować ich stały charakter (konwencja PEP 8).

class Matematyka:
    PI = 3.14159
    PODSTAWA = 10
    E = 2.71828

# W Pythonie atrybuty klasowe technicznie MOŻNA zmodyfikować,
# ale nie należy tego robić dla stałych koncepcyjnych.
# Matematyka.PI = 3.14  # zmieni globalnie dla KAŻDEGO kodu!

# Użycie stałej klasowej:
print(Matematyka.PI * 2)  # 6.28318

Konwencja nazewnicza: atrybuty klasowe pełniące rolę stałych zapisuj WIELKIMI LITERAMI z podkreślnikami ( MAKSYMALNA_LICZBA_UZYTKOWNIKOW ). To standard PEP 8.

Niezmienność

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

42 / 50
Podsumowanie stanu obiektu

Najważniejsze fakty

  • Stan obiektu = zbiór bieżących wartości atrybutów instancji
  • Stan zmienia się w czasie życia obiektu przez wywoływanie metod
  • Atrybuty klasowe są współdzielone - nie należą do stanu pojedynczego obiektu
  • Każdy obiekt ma niezależny, izolowany stan - brak skutków ubocznych
  • Metody są interfejsem do kontrolowanej zmiany stanu (enkapsulacja)
  • __dict__ pozwala podejrzeć aktualny stan w dowolnym momencie
  • Stan obiektu można serializować do słownika (obj.__dict__) i odtwarzać
  • Dobrze zaprojektowana klasa chroni przed niepoprawnymi stanami (walidacja w metodach)
Podsumowanie stanu

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

43 / 50
Praktyczny przykład: Licznik

Klasa Licznik- praktyczne zastosowanie atrybutów

Zaprojektujemy klasę, która ilustruje różnicę między atrybutami klasowymi i instancyjnymi w praktyce. Klasa Licznik :

  • Używa atrybutu klasowego do zliczania wszystkich utworzonych obiektów - wspólny licznik globalny
  • Używa atrybutów instancji do przechowywania unikalnego numeru i bieżącej liczby wywołań - stan indywidualny
  • Pokazuje, jak atrybut klasowy może służyć jako generator unikalnych identyfikatorów
  • Demonstruje niezależność liczników wywołań dla różnych obiektów

To klasyczny wzorzec projektowy, który możesz wykorzystać w swoich aplikacjach np. do nadawania numerów faktur, ID sesji, czy identyfikatorów w bazie danych.

Licznik

Slajd zatytułowany "Praktyczny przykład: Licznik" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

44 / 50
Kod: definicja z atrybutem klasowym i instancyjnym
class Licznik:
    # atrybut klasowy - wspólny dla wszystkich instancji
    liczba_obiektow = 0

    def __init__(self):
        # Zwiększamy licznik klasowy przy każdym nowym obiekcie
        # Używamy Licznik.liczba_obiektow, NIE self.liczba_obiektow!
        Licznik.liczba_obiektow += 1

        # atrybuty instancji - każdy obiekt ma swoje
        self.numer = Licznik.liczba_obiektow  # unikalny ID
        self.wywolania = 0  # licznik użyć tej instancji

    def uzyj(self):
        """Zwiększa licznik użyć tej instancji i zwraca opis."""
        self.wywolania += 1
        return f"Licznik #{self.numer} użyty {self.wywolania} raz(y)"
Definicja Licznik

Programowanie obiektowe wywodzi się z języka Simula z lat 60. XX wieku, a jego rozwój przyspieszył w latach 70. wraz z językiem Smalltalk, który wprowadził wiele koncepcji używanych do dziś, takich jak klasy, metody i dziedziczenie. Współcześnie OOP jest wspierane przez większość popularnych języków programowania, choć każdy z nich implementuje ten paradygmat nieco inaczej. Python przyjął podejście pragmatyczne, w którym wszystko jest obiektem, ale programista nie jest zmuszany do obiektowego stylu. Dzięki temu Python jest językiem wieloparadygmatowym, łączącym OOP z programowaniem proceduralnym i funkcyjnym w elastyczny sposób. Zrozumienie genezy i ewolucji OOP pomaga docenić jego zalety i świadomie stosować go we własnych projektach programistycznych.

Cztery filary OOP - enkapsulacja, dziedziczenie, polimorfizm i abstrakcja - są ze sobą ściśle powiązane i wzajemnie się uzupełniają. Enkapsulacja chroni stan obiektu przed niekontrolowanym dostępem z zewnątrz, co zwiększa bezpieczeństwo i spójność danych. Dziedziczenie pozwala na budowanie hierarchii klas i wielokrotne wykorzystanie kodu bez jego kopiowania. Polimorfizm umożliwia jednolity interfejs dla różnych typów obiektów, co upraszcza projektowanie rozszerzalnych systemów. Abstrakcja koncentruje się na istotnych cechach, pomijając nieistotne szczegóły implementacyjne. Opanowanie tych czterech koncepcji stanowi fundament biegłości w OOP.

45 / 50
Kod: tworzenie wielu liczników
# Tworzymy kilka liczników - każdy dostanie unikalny numer
l1 = Licznik()
l2 = Licznik()
l3 = Licznik()

# Każdy obiekt ma UNIKALNY numer (atrybut instancji)
print(l1.numer)  # 1
print(l2.numer)  # 2
print(l3.numer)  # 3

# Atrybut klasowy Licznik.liczba_obiektow rósł z każdym obiektem
print(Licznik.liczba_obiektow)  # 3

# Każdy licznik zaczyna od 0 wywołań
print(l1.wywolania)  # 0
print(l2.wywolania)  # 0
Wiele liczników

Tworzenie obiektu przez wywołanie klasy to jeden z najważniejszych wzorców w Pythonie. Składnia NazwaKlasy() może być myląca dla początkujących, ponieważ wygląda jak wywołanie funkcji, ale w rzeczywistości uruchamia złożony proces alokacji i inicjalizacji. Python, jako język dynamiczny, pozwala na tworzenie obiektów w dowolnym momencie działania programu, w tym wewnątrz pętli, warunków czy wyrażeń listowych. Każde wywołanie klasy tworzy nowy, niezależny obiekt w pamięci, nawet jeżeli klasa nie ma zdefiniowanego konstruktora. Zmienna przechowuje referencję do obiektu, a nie sam obiekt, co ma kluczowe znaczenie dla zrozumienia mechanizmu przypisań i kopiowania.

Proces tworzenia obiektu składa się z kilku etapów: najpierw wywoływana jest metoda __new__, która alokuje pamięć dla nowej instancji, następnie uruchamiany jest __init__ do inicjalizacji atrybutów, a na końcu zwracana jest referencja do gotowego obiektu. Większość programistów modyfikuje wyłącznie __init__, pozostawiając __new__ w domyślnej implementacji. Zrozumienie tej kolejności jest ważne przy tworzeniu bardziej zaawansowanych wzorców, takich jak Singleton czy Fabryka. Gdy tworzymy wiele obiektów w pętli, każdy z nich jest niezależną instancją z własnym stanem i tożsamością.

46 / 50
Kod: atrybut klasowy jako licznik obiektów
class Licznik:
    liczba_obiektow = 0

    def __init__(self):
        Licznik.liczba_obiektow += 1
        self.numer = Licznik.liczba_obiektow
        self.wywolania = 0

    def uzyj(self):
        self.wywolania += 1
        return f"Licznik #{self.numer} użyty {self.wywolania} raz(y)"

# Demonstracja: klasowy licznik obiektów rosnący automatycznie
print("Przed: liczba obiektów =", Licznik.liczba_obiektow)  # 3 (po poprzednich)

liczniki = [Licznik() for _ in range(5)]
print(Licznik.liczba_obiektow)  # 8 = 3 + 5
print(liczniki[0].numer)      # 4 - kontynuacja numeracji
print(liczniki[4].numer)      # 8

# Atrybut klasowy liczba_obiektow niezależny od instancji
print(liczniki[0].__dict__)  # {'numer': 4, 'wywolania': 0} - brak 'liczba_obiektow'!
Licznik obiektów

Atrybuty są podstawowym mechanizmem przechowywania stanu obiektu i mogą zawierać dane dowolnego typu dostępnego w Pythonie. Każdy obiekt ma własną, niezależną kopię atrybutów zdefiniowanych w konstruktorze, co oznacza, że zmiana wartości w jednej instancji nie wpływa na pozostałe. W Pythonie atrybuty są przechowywane w słowniku __dict__ każdego obiektu, co umożliwia dynamiczne dodawanie i usuwanie atrybutów w czasie wykonania. Chociaż dynamiczne dodawanie atrybutów jest możliwe, w praktyce zaleca się definiowanie wszystkich atrybutów w konstruktorze __init__ dla zachowania przewidywalności kodu. Taki nawyk sprawia, że struktura obiektu jest jasna dla każdego programisty czytającego definicję klasy. Ponadto wiele narzędzi do statycznej analizy kodu wymaga jawnego deklarowania atrybutów.

Konstruktor __init__ jest wywoływany automatycznie po utworzeniu obiektu przez metodę __new__, która alokuje pamięć dla nowej instancji. Zadaniem __init__ jest przygotowanie obiektu do użycia poprzez ustawienie początkowych wartości atrybutów i wykonanie ewentualnych czynności inicjalizacyjnych. W przeciwieństwie do niektórych innych języków, Python nie obsługuje przeciążania konstruktorów - zamiast tego stosuje się parametry opcjonalne z wartościami domyślnymi oraz wzorzec fabryki. Parametr self, obowiązkowy w każdej metodzie instancyjnej, jest referencją do konkretnego obiektu, na którym wywołano metodę. Dzięki self metoda ma dostęp do wszystkich atrybutów i innych metod danego obiektu.

47 / 50
Kod: działanie krok po kroku
# Krok 1: tworzymy pierwszy licznik
l1 = Licznik()
# Licznik.liczba_obiektow = 1 (klasowy)
# l1.numer = 1, l1.wywolania = 0 (instancji)

# Krok 2: używamy go 3 razy - wywolania rośnie
print(l1.uzyj())  # Licznik #1 użyty 1 raz(y)
print(l1.uzyj())  # Licznik #1 użyty 2 raz(y)
print(l1.uzyj())  # Licznik #1 użyty 3 raz(y)

# Krok 3: drugi licznik ma własny, niezależny licznik wywołań
l2 = Licznik()
# Licznik.liczba_obiektow = 2 (klasowy wzrósł)
# l2.numer = 2, l2.wywolania = 0

print(l2.uzyj())  # Licznik #2 użyty 1 raz(y) - l2 dopiero zaczyna
print(l1.uzyj())  # Licznik #1 użyty 4 raz(y) - niezależny od l2!

# Krok 4: liczba obiektów globalna - atrybut klasowy
print(Licznik.liczba_obiektow) # 2 - utworzyliśmy 2 obiekty

# Sprawdźmy __dict__ obu obiektów
print(l1.__dict__)  # {'numer': 1, 'wywolania': 4}
print(l2.__dict__)  # {'numer': 2, 'wywolania': 1}
Krok po kroku

Slajd zatytułowany "Kod: działanie krok po kroku" przedstawia istotne zagadnienie z zakresu programowania obiektowego w Pythonie. Zrozumienie przedstawionych tu koncepcji jest niezbędne do dalszej nauki i praktycznego stosowania OOP w codziennej pracy programisty. Zaleca się dokładne przeanalizowanie przykładów kodu i samodzielne ich przetestowanie w środowisku REPL lub edytorze. Warto również zwrócić uwagę na powiązania między tym tematem a innymi zagadnieniami omawianymi w kursie. Systematyczne budowanie wiedzy krok po kroku to klucz do opanowania programowania obiektowego w Pythonie. Nie pomijaj żadnego slajdu, nawet jeśli wydaje Ci się, że temat jest Ci już znany - powtórka utrwala wiedzę i pozwala dostrzec nowe szczegóły w znajomym materiale.

Zachęcamy do samodzielnego eksperymentowania z omawianymi mechanizmami i modyfikowania przykładowego kodu. Praktyczne ćwiczenia i własne projekty to najskuteczniejsza metoda nauki programowania, ponieważ wymagają aktywnego stosowania wiedzy. Pamiętaj, że błędy są naturalną częścią procesu uczenia się i każda pomyłka przybliża Cię do mistrzostwa. Warto prowadzić własny dziennik błędów, w którym zapisujesz napotkane problemy i ich rozwiązania. Taki zeszyt stanie się z czasem bezcennym źródłem wiedzy i referencją na przyszłość. Korzystaj z dokumentacji oficjalnej Pythona oraz społeczności programistycznych - to nieocenione źródła wiedzy i wsparcia w trudnych momentach.

48 / 50
Podsumowanie przykładu Licznik

Czego się nauczyliśmy?

  • Atrybut klasowy liczba_obiektow jest wspólny dla wszystkich instancji - jeden licznik globalny
  • Każdy obiekt ma własne atrybuty instancji: numer, wywolania - stan indywidualny
  • Modyfikacja atrybutu klasowego przez klasę ( Licznik.liczba_obiektow += 1 ) działa globalnie dla wszystkich
  • Metoda uzyj() modyfikuje stan instancji (self.wywolania) - tylko dla tego obiektu
  • Atrybuty instancji są niezależne - l1 i l2 mają osobne liczniki wywołań, nie wpływają na siebie
  • Atrybut klasowy NIE pojawia się w __dict__ instancji - to dowód, że nie jest kopiowany
  • Wzorzec Licznik można wykorzystać do automatycznego nadawania ID w systemach bazodanowych
Podsumowanie Licznik

Podsumowanie to dobry moment na refleksję nad przyswojonym materiałem i identyfikację obszarów wymagających dodatkowej pracy. Wymienione punkty stanowią esencję przerobionej części kursu - od definicji klasy, przez tworzenie obiektów, aż po kompozycję i wzorzec fabryki. Każdy z tych punktów będzie rozwijany w kolejnych modułach, dlatego warto upewnić się, że są dobrze zrozumiane. Zachęcamy do tworzenia własnych notatek i map myśli, które pomagają w usystematyzowaniu wiedzy. Regularne powtórki są kluczowe dla trwałego zapamiętania materiału.

Mapy myśli są skutecznym narzędziem wizualizacji złożonych koncepcji i relacji między nimi. Przedstawiona mapa obrazuje najważniejsze pojęcia omówione w tej części kursu oraz ich wzajemne powiązania. Tworzenie własnych map myśli podczas nauki programowania aktywuje inne obszary mózgu niż czytanie liniowego tekstu, co przekłada się na lepsze zapamiętywanie. Studenci, którzy regularnie tworzą mapy myśli, osiągają lepsze wyniki w testach koncepcyjnych i szybciej łączą nowe informacje z już posiadaną wiedzą. Zachęcamy do wykorzystania tej techniki.

49 / 50
Mapa myśli

Atrybuty i metody - mapa koncepcyjna

            +===========================================+
            |      Atrybuty i metody w Pythonie OOP     |
            +===========================================+
                              |
            +-----------------+------------------+
            |                                    |
    =========================          ==========================
    Atrybuty instancji                Atrybuty klasowe
    =========================          ==========================
    - self.atrybut = X                - Klasa.atrybut = X
    - każdy obiekt ma kopię         - współdzielone przez wszystkie
    - definiowane w __init__          - definiowane w ciele klasy
    - stan indywidualny obiektu       - stałe / liczniki / konfiguracja
    - obiekt.__dict__                 - Klasa.__dict__
            |                                    |
    =========================          ==========================
    Metody instancji                  Funkcje pomocnicze
    =========================          ==========================
    - self jako 1. parametr           - hasattr(obiekt, "nazwa")
    - odczytują self.atrybut         - getattr(obiekt, "nazwa", def)
    - modyfikują self.atrybut = X    - setattr(obiekt, "nazwa", X)
    - wywołują self.inna_metoda()    - del obiekt.atrybut
            |                                    |
    =========================          ==========================
    Ważne koncepcje                  Złote zasady
    =========================          ==========================
    - Shadowing (przesłanianie)       - Klasowe modyfikuj przez klasę
    - Każdy obiekt niezależny        - Instancyjne przez self
    - Stan zmienia się w czasie       - Nie myl przypisania z odczytem
Mapa myśli

Podsumowanie to dobry moment na refleksję nad przyswojonym materiałem i identyfikację obszarów wymagających dodatkowej pracy. Wymienione punkty stanowią esencję przerobionej części kursu - od definicji klasy, przez tworzenie obiektów, aż po kompozycję i wzorzec fabryki. Każdy z tych punktów będzie rozwijany w kolejnych modułach, dlatego warto upewnić się, że są dobrze zrozumiane. Zachęcamy do tworzenia własnych notatek i map myśli, które pomagają w usystematyzowaniu wiedzy. Regularne powtórki są kluczowe dla trwałego zapamiętania materiału.

Mapy myśli są skutecznym narzędziem wizualizacji złożonych koncepcji i relacji między nimi. Przedstawiona mapa obrazuje najważniejsze pojęcia omówione w tej części kursu oraz ich wzajemne powiązania. Tworzenie własnych map myśli podczas nauki programowania aktywuje inne obszary mózgu niż czytanie liniowego tekstu, co przekłada się na lepsze zapamiętywanie. Studenci, którzy regularnie tworzą mapy myśli, osiągają lepsze wyniki w testach koncepcyjnych i szybciej łączą nowe informacje z już posiadaną wiedzą. Zachęcamy do wykorzystania tej techniki.

50 / 50
Quiz - sprawdź swoją wiedzę

Pytania sprawdzające

Pytanie 1: Co się stanie, gdy wykonasz obiekt.atrybut_klasowy = 5?

A) Zmodyfikuje atrybut klasowy dla wszystkich obiektów

B) Utworzy nowy atrybut instancji o nazwie atrybut_klasowy

C) Rzuci wyjątek TypeError

Odpowiedź: B. Przypisanie przez instancję ZAWSZE tworzy/modyfikuje atrybut instancji.

Pytanie 2: Która funkcja pozwala odczytać atrybut z wartością domyślną, jeżeli atrybut nie istnieje?

A)hasattr()

B)setattr()

C)getattr()

Odpowiedź: C.getattr(obiekt, "nazwa", domyslna) zwraca domyślną, jeśli atrybut nie istnieje.

Pytanie 3: Gdzie znajdziesz atrybuty instancji konkretnego obiektu?

A) W Klasa.__dict__

B) W obiekt.__dict__

C) W zmiennej globalnej

Odpowiedź: B. obiekt.__dict__ zawiera atrybuty instancji. Klasa.__dict__ zawiera atrybuty klasowe i metody.

Pytanie 4: Jaka jest różnica między Pracownik.podwyzka = 1.10 a p.podwyzka = 1.10 ?

A) To samo - obie linie modyfikują atrybut klasowy

B) Pierwsze modyfikuje atrybut klasowy, drugie tworzy atrybut instancji

C) Pierwsze rzuci błąd, drugie zadziała

Odpowiedź: B. Przez klasę → zmiana globalna. Przez instancję → nowy atrybut instancji.

Quiz

Quiz podsumowujący to nie tylko sprawdzenie wiedzy, ale także okazja do utrwalenia najważniejszych koncepcji poprzez aktywne przypominanie sobie materiału. Badania z zakresu neurodydaktyki pokazują, że testowanie własnej wiedzy jest jedną z najskuteczniejszych metod uczenia się, znacznie efektywniejszą niż bierne czytanie. Każde pytanie quizu zostało starannie zaprojektowane, aby sprawdzić zrozumienie konkretnego zagadnienia, a nie tylko pamięciowe odtworzenie definicji. Jeżeli któreś pytanie sprawiło Ci trudność, potraktuj to jako sygnał, że dany temat wymaga powtórzenia. Wróć do odpowiedniego slajdu i przeanalizuj go jeszcze raz, tym razem zwracając uwagę na szczegóły, które mogły Ci umknąć.

Sukces w nauce programowania polega na systematycznym budowaniu wiedzy - każda kolejna część opiera się na fundamentach z części poprzednich. Jeżeli masz wątpliwości co do którejkolwiek koncepcji, nie przechodź dalej, dopóki jej nie wyjaśnisz. Wykorzystaj dostępne zasoby: dokumentację Pythona, fora społecznościowe, tutoriale wideo i oczywiście możliwość eksperymentowania we własnym środowisku programistycznym. Pamiętaj, że każdy zaawansowany programista zaczynał od podstaw i pokonał te same trudności co Ty teraz. Gratulujemy ukończenia kolejnego etapu nauki i życzymy powodzenia w dalszej części kursu.