PL: Nauczyć się podstaw programowania w Pythonie: zmienne, typy danych, pętle, funkcje, klasy, type hints oraz najlepsze praktyki (DRY, KISS, Zen of Python). EN: Learn the fundamentals of Python programming: variables, data types, loops, functions, classes, type hints, and best practices (DRY, KISS, Zen of Python).
Before you start, create a proper project structure and initialize Git — this lets you practice skills from Phase 0!
Zanim zaczniesz, stwórz odpowiednią strukturę projektu i zainicjuj Git — to pozwala przećwiczyć umiejętności z Fazy 0!
🧩 Zadanie / Task:
Step 1: Create project folder / Krok 1: Utwórz folder projektu
# Navigate to your learning directory / Przejdź do katalogu nauki
# Replace with your own path, for example:
# Windows: cd C:\learning\python
# Mac/Linux: cd ~/learning/python
cd your-learning-directory
# Create Phase 1 folder / Utwórz folder dla Fazy 1
mkdir 01_basics
cd 01_basicsStep 2: Initialize uv project / Krok 2: Zainicjuj projekt uv
# Initialize new Python project with uv / Zainicjuj nowy projekt Pythona z uv
uv init
# This creates: / To tworzy:
# - pyproject.toml (dependency file / plik zależności)
# - .python-version (Python version / wersja Pythona)
# - hello.py (example file / przykładowy plik)Step 3: Initialize Git repository (if not already in one) / Krok 3: Zainicjuj repozytorium Git
# Check if you're already in a Git repo / Sprawdź czy jesteś już w repo Git
git status
# If not, initialize Git / Jeśli nie, zainicjuj Git
git init
# Create .gitignore if you don't have one / Utwórz .gitignore jeśli go nie masz
cat > .gitignore << 'EOF'
# Python
__pycache__/
*.py[cod]
.Python
.venv/
venv/
.env
*.log
# IDE
.idea/
.vscode/
EOF
# Add initial files / Dodaj początkowe pliki
git add .
git commit -m "Initialize Phase 1 - Python Basics project"Step 4: Create README for this phase / Krok 4: Utwórz README dla tej fazy
Create a file README.md:
Utwórz plik README.md:
# Phase 1 — Python Basics
This folder contains exercises for learning Python fundamentals.
Ten folder zawiera ćwiczenia do nauki podstaw Pythona.
## Files / Pliki
- `basics.py` — Variables and data types / Zmienne i typy danych
- `collections.py` — Lists, dicts, tuples, sets / Listy, słowniki, tuple, zbiory
- `control_flow.py` — if/elif/else, match-case, None checking / Instrukcje warunkowe, match-case, sprawdzanie None
- `loops.py` — for and while loops / Pętle for i while
- `functions.py` — Functions with type hints / Funkcje z type hints
- `classes.py` — Classes, OOP, LSP examples / Klasy, OOP, przykłady LSP
- `type_hints.py` — Type hints / Wskazówki typów
- `dataclass_demo.py` — Dataclasses / Klasy danych
- `pydantic_demo.py` — Pydantic (alternative to dataclass) / Pydantic (alternatywa dla dataclass)
- `task_manager.py` — Mini-project / Mini-projekt
## Running the code / Uruchamianie kodu
```bash
# Run any file with uv / Uruchom dowolny plik z uv
uv run python basics.py
uv run python collections.py
# etc.- Variables and data types
- Collections
- Control flow (if/elif/else, match-case, None checking)
- Loops
- Functions (with type hints)
- Classes and OOP (with LSP)
- Type hints
- Dataclasses
- Pydantic (optional alternative)
- Mini-project
Save and commit:
Zapisz i commituj:
```bash
git add README.md
git commit -m "Add Phase 1 README with file structure"
Expected result / Oczekiwany rezultat:
You now have a clean project structure ready for Python exercises.
Masz teraz czystą strukturę projektu gotową na ćwiczenia Pythona.
A variable is like a labeled box that stores information — you give it a name and put data inside.
Zmienna to jak opisane pudełko, w którym przechowujesz informacje — dajesz jej nazwę i wkładasz do środka dane.
Python has several built-in data types for storing different kinds of information:
Python ma kilka wbudowanych typów danych do przechowywania różnych rodzajów informacji:
- int — whole numbers / liczby całkowite (e.g.,
42,-10) - float — decimal numbers / liczby zmiennoprzecinkowe (e.g.,
3.14,-0.5) - str — text / tekst (e.g.,
"Hello",'Python') - bool — truth values / wartości logiczne (
True,False) - list — ordered collections / uporządkowane kolekcje (e.g.,
[1, 2, 3]) - dict — key-value pairs / pary klucz-wartość (e.g.,
{"name": "Alice"}) - tuple — immutable ordered collections / niemutowalne kolekcje (e.g.,
(1, 2, 3)) - set — unordered collections of unique items / nieuporządkowane zbiory unikalnych elementów (e.g.,
{1, 2, 3})
📘 Official documentation / Oficjalna dokumentacja:
Read sections: "Truth Value Testing", "Numeric Types", and "Text Sequence Type" / Przeczytaj sekcje: "Truth Value Testing", "Numeric Types" i "Text Sequence Type"
🎥 Video / Wideo:
Corey Schafer – Python Tutorial: Variables and Data Types (0:00–15:00)
Covers variables, numbers, strings, and basic operations / Omawia zmienne, liczby, stringi i podstawowe operacje
🧩 Zadania / Tasks:
Create a file basics.py:
Utwórz plik basics.py:
# Numbers / Liczby
age = 25 # int / liczba całkowita
height = 1.75 # float / liczba zmiennoprzecinkowa
temperature = -5 # negative int / ujemna liczba całkowita
# Text / Tekst
name = "Alice" # str / string
greeting = 'Hello' # single quotes work too / pojedyncze cudzysłowy też działają
# Boolean / Wartości logiczne
is_student = True # bool
has_license = False # bool
# Print with type information / Wyświetl z informacją o typie
print(f"{age=}, type: {type(age)}")
print(f"{height=}, type: {type(height)}")
print(f"{name=}, type: {type(name)}")
print(f"{is_student=}, type: {type(is_student)}")
# Basic operations / Podstawowe operacje
sum_result = 10 + 5
difference = 10 - 3
product = 4 * 3
quotient = 15 / 3 # float division / dzielenie zwraca float
floor_div = 15 // 4 # integer division / dzielenie całkowite
remainder = 15 % 4 # modulo / reszta z dzielenia
power = 2 ** 3 # exponentiation / potęgowanie
print(f"\n{sum_result=}, {difference=}, {product=}")
print(f"{quotient=}, {floor_div=}, {remainder=}, {power=}")
# String operations / Operacje na stringach
full_name = name + " Smith" # concatenation / łączenie
repeated = "Ha" * 3 # repetition / powtarzanie
length = len(name) # length / długość
print(f"\n{full_name=}, {repeated=}, {length=}")Run the program:
Uruchom program:
uv run python basics.pyExpected output / Oczekiwany rezultat:
age=25, type: <class 'int'>
height=1.75, type: <class 'float'>
name='Alice', type: <class 'str'>
is_student=True, type: <class 'bool'>
sum_result=15, difference=7, product=12
quotient=5.0, floor_div=3, remainder=3, power=8
full_name='Alice Smith', repeated='HaHaHa', length=5
💾 Save to Git / Zapisz w Git:
After completing this exercise, save your work to Git:
Po ukończeniu tego ćwiczenia, zapisz pracę w Git:
git add basics.py
git commit -m "Add basics.py - variables and data types exercise"Collections store multiple items — like containers that organize related data.
Kolekcje przechowują wiele elementów — jak pojemniki organizujące powiązane dane.
🧩 Zadanie / Task:
Create a file collections.py:
Utwórz plik collections.py:
# List — ordered, mutable / uporządkowana, mutowalna
fruits = ["apple", "banana", "cherry"]
fruits.append("orange") # add item / dodaj element
fruits[0] = "apricot" # change item / zmień element
print(f"Fruits: {fruits}")
print(f"First fruit: {fruits[0]}")
print(f"Last fruit: {fruits[-1]}")
# Tuple — ordered, immutable / uporządkowana, niemutowalna
coordinates = (10, 20)
# coordinates[0] = 15 # This would raise an error! / To spowodowałoby błąd!
x, y = coordinates # unpacking / rozpakowywanie
print(f"\nCoordinates: {coordinates}, x={x}, y={y}")
# Dictionary — key-value pairs / pary klucz-wartość
person = {
"name": "Alice",
"age": 25,
"city": "Warsaw"
}
person["email"] = "alice@example.com" # add new key / dodaj nowy klucz
print(f"\nPerson: {person}")
print(f"Name: {person['name']}")
print(f"Age: {person.get('age')}")
# Set — unique items, unordered / unikalne elementy, nieuporządkowane
numbers = {1, 2, 3, 3, 4, 4} # duplicates removed / duplikaty usunięte
numbers.add(5)
print(f"\nNumbers: {numbers}")
# Common operations / Powszechne operacje
print(f"\nList length: {len(fruits)}")
print(f"Is 'apple' in fruits? {'apple' in fruits}")
print(f"Is 'grape' in fruits? {'grape' in fruits}")Run the program:
Uruchom program:
uv run python collections.pyExpected output / Oczekiwany rezultat:
Fruits: ['apricot', 'banana', 'cherry', 'orange']
First fruit: apricot
Last fruit: orange
Coordinates: (10, 20), x=10, y=20
Person: {'name': 'Alice', 'age': 25, 'city': 'Warsaw', 'email': 'alice@example.com'}
Name: Alice
Age: 25
Numbers: {1, 2, 3, 4, 5}
List length: 4
Is 'apple' in fruits? False
Is 'grape' in fruits? False
💾 Save to Git / Zapisz w Git:
git add collections.py
git commit -m "Add collections.py - lists, dicts, tuples, sets"Control flow lets your program make decisions and repeat actions — like following a recipe that says "if eggs are cold, wait 5 minutes; otherwise, continue."
Kontrola przepływu pozwala programowi podejmować decyzje i powtarzać działania — jak przepis mówiący "jeśli jajka są zimne, poczekaj 5 minut; w przeciwnym razie kontynuuj."
Conditional statements with if, elif, and else let you make decisions based on conditions.
Instrukcje warunkowe if, elif i else pozwalają podejmować decyzje na podstawie warunków.
📘 Documentation / Dokumentacja:
Read sections: "if Statements" / Przeczytaj sekcję: "if Statements"
🎥 Video / Wideo:
Corey Schafer – Conditionals and Booleans (0:00–20:00)
Explains if/elif/else and comparison operators / Wyjaśnia if/elif/else i operatory porównania
🧩 Zadanie / Task:
Create a file control_flow.py:
Utwórz plik control_flow.py:
# Simple if statement / Prosta instrukcja if
age = 20
if age >= 18:
print("You are an adult") # Jesteś dorosły
else:
print("You are a minor") # Jesteś niepełnoletni
# Multiple conditions / Wiele warunków
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
print(f"Score: {score}, Grade: {grade}")
# Match-case statement (Python 3.10+) — like switch in other languages
# Match-case (Python 3.10+) — jak switch w innych językach
status_code = 200
match status_code:
case 200:
message = "OK"
case 404:
message = "Not Found"
case 500:
message = "Server Error"
case _: # default case / domyślny przypadek
message = "Unknown Status"
print(f"\nStatus {status_code}: {message}")
# Match-case with ranges (combining with if) / Match-case z zakresami (łącząc z if)
grade_score = 85
match grade_score:
case score if score >= 90:
grade_letter = "A"
case score if score >= 80:
grade_letter = "B"
case score if score >= 70:
grade_letter = "C"
case _:
grade_letter = "F"
print(f"Grade score {grade_score}: {grade_letter}")
# Comparison operators / Operatory porównania
x = 10
y = 20
print(f"\n{x} == {y}: {x == y}") # equal / równe
print(f"{x} != {y}: {x != y}") # not equal / nierówne
print(f"{x} < {y}: {x < y}") # less than / mniejsze
print(f"{x} > {y}: {x > y}") # greater than / większe
print(f"{x} <= {y}: {x <= y}") # less or equal / mniejsze lub równe
print(f"{x} >= {y}: {x >= y}") # greater or equal / większe lub równe
# Logical operators / Operatory logiczne
is_student = True
has_id = True
if is_student and has_id:
print("\nYou get a student discount") # Dostajesz zniżkę studencką
is_weekend = False
is_holiday = True
if is_weekend or is_holiday:
print("Time to relax!") # Czas na odpoczynek!
is_raining = True
if not is_raining:
print("Let's go outside!") # Chodźmy na zewnątrz!
else:
print("Stay inside") # Zostań w środku
# Checking for None — use 'is' not '==' / Sprawdzanie None — użyj 'is' nie '=='
# 'is' checks identity (same object), '==' checks equality (same value)
# 'is' sprawdza tożsamość (ten sam obiekt), '==' sprawdza równość (ta sama wartość)
# For None, always use 'is' — it's more precise and idiomatic
# Dla None, zawsze używaj 'is' — jest bardziej precyzyjne i idiomatyczne
user_name = None
default_name = "Guest"
if user_name is None:
print(f"\nWelcome, {default_name}!") # Witaj, Gościu!
else:
print(f"\nWelcome, {user_name}!")
# Also check 'is not None' / Również sprawdź 'is not None'
email = "user@example.com"
if email is not None:
print(f"Sending email to: {email}") # Wysyłanie emaila do: {email}Run the program:
Uruchom program:
uv run python control_flow.pyExpected output / Oczekiwany rezultat:
You are an adult
Score: 85, Grade: B
Status 200: OK
Grade score 85: B
10 == 20: False
10 != 20: True
10 < 20: True
10 > 20: False
10 <= 20: True
10 >= 20: False
You get a student discount
Time to relax!
Stay inside
Welcome, Guest!
Sending email to: user@example.com
💾 Save to Git / Zapisz w Git:
git add control_flow.py
git commit -m "Add control_flow.py - if/elif/else, match-case, logical operators, None checking"Loops repeat actions — like doing dishes one by one until the sink is empty.
Pętle powtarzają działania — jak zmywanie naczyń jedno po drugim, aż zlew będzie pusty.
I find for loops work well when you know how many times to repeat or have a collection to iterate through.
Stwierdziłem, że pętle for działają dobrze, gdy wiesz ile razy powtórzyć lub masz kolekcję do iteracji.
I reach for while loops when I don't know in advance how many iterations will be needed.
Sięgam po pętle while, gdy nie wiem z góry ile iteracji będzie potrzebnych.
🎥 Video / Wideo:
Corey Schafer – Python Loops Tutorial (0:00–25:00)
Covers for and while loops with examples / Omawia pętle for i while z przykładami
🧩 Zadanie / Task:
Create a file loops.py:
Utwórz plik loops.py:
# For loop with list / Pętla for z listą
fruits = ["apple", "banana", "cherry"]
print("Fruits:")
for fruit in fruits:
print(f" - {fruit}")
# For loop with range / Pętla for z zakresem
print("\nNumbers 0-4:")
for i in range(5): # 0, 1, 2, 3, 4
print(i, end=" ")
print("\n\nNumbers 1-5:")
for i in range(1, 6): # 1, 2, 3, 4, 5
print(i, end=" ")
print("\n\nEven numbers 0-10:")
for i in range(0, 11, 2): # start, stop, step
print(i, end=" ")
# For loop with enumerate / Pętla for z enumerate
print("\n\nFruits with index:")
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# For loop with dictionary / Pętla for ze słownikiem
person = {"name": "Alice", "age": 25, "city": "Warsaw"}
print("\nPerson details:")
for key, value in person.items():
print(f"{key}: {value}")
# While loop / Pętla while
count = 0
print("\nCounting to 5:")
while count < 5:
print(count, end=" ")
count += 1
# Break and continue / Break i continue
print("\n\nFinding first even number:")
numbers = [1, 3, 5, 8, 9, 10]
for num in numbers:
if num % 2 == 0:
print(f"Found: {num}")
break # exit loop / wyjdź z pętli
print("\nSkipping odd numbers:")
for num in range(10):
if num % 2 != 0:
continue # skip this iteration / pomiń tę iterację
print(num, end=" ")
print() # new line / nowa liniaRun the program:
Uruchom program:
uv run python loops.pyExpected output / Oczekiwany rezultat:
Fruits:
- apple
- banana
- cherry
Numbers 0-4:
0 1 2 3 4
Numbers 1-5:
1 2 3 4 5
Even numbers 0-10:
0 2 4 6 8 10
Fruits with index:
0: apple
1: banana
2: cherry
Person details:
name: Alice
age: 25
city: Warsaw
Counting to 5:
0 1 2 3 4
Finding first even number:
Found: 8
Skipping odd numbers:
0 2 4 6 8
💾 Save to Git / Zapisz w Git:
git add loops.py
git commit -m "Add loops.py - for and while loops with break/continue"A function is a reusable block of code that performs a specific task — like a recipe you can follow multiple times.
Funkcja to wielokrotnego użytku blok kodu, który wykonuje konkretne zadanie — jak przepis, którego możesz używać wielokrotnie.
Functions help you:
Funkcje pomagają Ci:
- Avoid repetition (DRY principle) / Unikać powtórzeń (zasada DRY)
- Organize code into logical chunks / Organizować kod w logiczne części
- Make code easier to test and debug / Ułatwiać testowanie i debugowanie kodu
📘 Documentation / Dokumentacja:
Read sections: "Defining Functions" and "Default Argument Values" / Przeczytaj sekcje: "Defining Functions" i "Default Argument Values"
🎥 Video / Wideo:
Corey Schafer – Python Functions (0:00–30:00)
Covers function basics, parameters, return values, and scope / Omawia podstawy funkcji, parametry, wartości zwracane i zakres
🧩 Zadanie / Task:
Create a file functions.py:
Utwórz plik functions.py:
from typing import List, Tuple, Any
# Simple function / Prosta funkcja
def greet() -> None:
print("Hello, World!")
greet()
# Function with parameters / Funkcja z parametrami
def greet_person(name: str) -> None:
print(f"Hello, {name}!")
greet_person("Alice")
greet_person("Bob")
# Function with return value / Funkcja ze zwracaną wartością
def add(a: int, b: int) -> int:
return a + b
result = add(5, 3)
print(f"\n5 + 3 = {result}")
# Function with default parameters / Funkcja z domyślnymi parametrami
def greet_with_title(name: str, title: str = "Mr.") -> str:
return f"Hello, {title} {name}!"
print(greet_with_title("Smith"))
print(greet_with_title("Smith", "Dr."))
# Function with multiple return values / Funkcja zwracająca wiele wartości
def get_min_max(numbers: List[int]) -> Tuple[int, int]:
return min(numbers), max(numbers)
nums = [3, 7, 1, 9, 2]
minimum, maximum = get_min_max(nums)
print(f"\nNumbers: {nums}")
print(f"Min: {minimum}, Max: {maximum}")
# Function with *args (variable arguments) / Funkcja z *args (zmienne argumenty)
def sum_all(*numbers: int) -> int:
total = 0
for num in numbers:
total += num
return total
print(f"\nsum_all(1, 2, 3) = {sum_all(1, 2, 3)}")
print(f"sum_all(1, 2, 3, 4, 5) = {sum_all(1, 2, 3, 4, 5)}")
# Function with **kwargs (keyword arguments) / Funkcja z **kwargs (argumenty nazwane)
def print_info(**info: Any) -> None:
for key, value in info.items():
print(f"{key}: {value}")
print("\nUser info:")
print_info(name="Alice", age=25, city="Warsaw")
# Docstrings — function documentation / Docstringi — dokumentacja funkcji
def calculate_area(width: float, height: float) -> float:
"""
Calculate rectangle area.
Args:
width: Rectangle width in meters
height: Rectangle height in meters
Returns:
Area in square meters
"""
return width * height
# Access docstring / Dostęp do docstringa
print(f"\n{calculate_area.__doc__}")
print(f"Area: {calculate_area(5, 3)} m²")Run the program:
Uruchom program:
uv run python functions.pyExpected output / Oczekiwany rezultat:
Hello, World!
Hello, Alice!
Hello, Bob!
5 + 3 = 8
Hello, Mr. Smith!
Hello, Dr. Smith!
Numbers: [3, 7, 1, 9, 2]
Min: 1, Max: 9
sum_all(1, 2, 3) = 6
sum_all(1, 2, 3, 4, 5) = 15
User info:
name: Alice
age: 25
city: Warsaw
Calculate rectangle area.
Args:
width: Rectangle width in meters
height: Rectangle height in meters
Returns:
Area in square meters
Area: 15 m²
💾 Save to Git / Zapisz w Git:
git add functions.py
git commit -m "Add functions.py - functions, parameters, docstrings"Lambda functions are small, anonymous functions — I find them handy for simple operations where writing a full function would be overkill.
Funkcje lambda to małe, anonimowe funkcje — uważam je za przydatne do prostych operacji, gdzie pisanie pełnej funkcji byłoby przesadą.
🧩 Zadanie / Task:
Add to functions.py:
Dodaj do functions.py:
# Lambda function / Funkcja lambda
square = lambda x: x ** 2
print(f"\nSquare of 5: {square(5)}")
# Lambda with multiple parameters / Lambda z wieloma parametrami
add = lambda a, b: a + b
print(f"3 + 4 = {add(3, 4)}")
# Lambda with map / Lambda z map
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(f"\nNumbers: {numbers}")
print(f"Squared: {squared}")
# Lambda with filter / Lambda z filter
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Even numbers: {even_numbers}")
# Lambda with sorted / Lambda z sorted
people = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 20}
]
sorted_by_age = sorted(people, key=lambda person: person["age"])
print(f"\nSorted by age:")
for person in sorted_by_age:
print(f" {person['name']}: {person['age']}")💾 Update Git / Zaktualizuj Git:
You've added lambda functions to functions.py. Update your commit:
Dodałeś funkcje lambda do functions.py. Zaktualizuj commit:
git add functions.py
git commit -m "Update functions.py - add lambda functions and map/filter"Napisz program, który symuluje paradoks Monty'ego Halla.
Write a program that simulates the Monty Hall problem.
There are three doors: one has a prize, two have zonks.
Są trzy drzwi: za jednymi jest nagroda, za dwoma zonk.
Player chooses one door.
Gracz wybiera jedne drzwi.
Host opens one of the remaining doors, revealing a zonk (never the prize!).
Prowadzący otwiera jedne z pozostałych drzwi, pokazując zonk (nigdy nagrody!).
Player can switch their choice or stay with the original.
Gracz może zmienić swój wybór lub pozostać przy oryginalnym.
Create a file game.py to simulate two strategies:
Utwórz plik game.py aby zasymulować dwie strategie:
Scenario 1: Player always stays with their original choice
Scenariusz 1: Gracz zawsze pozostaje przy swoim oryginalnym wyborze
Scenario 2: Player always switches doors
Scenariusz 2: Gracz zawsze zmienia drzwi
Run 1000 simulation rounds for each scenario. What % of wins does the player get in each scenario?
Przeprowadź 1000 rund symulacji dla każdego scenariusza. Jaki % wygranych ma gracz w każdym scenariuszu?
Find the right library for randomization (check Python docs for the random module).
Znajdź odpowiednią bibliotekę do losowania (sprawdź dokumentację Pythona o module random).
Think about how to represent the game steps in code.
Przemyśl, jak przedstawić kolejne kroki gry w kodzie.
How to collect results for 1 run, then for multiple runs?
Jak zebrać wyniki dla 1 wykonania, a później dla wielu?
Use the debugger if needed.
Używaj debugera, jeśli potrzebujesz.
Don't ask AI for code*
Nie pytaj AI o kod
git add game.py
git commit -m "Add game.py - Monty Hall simulation"After completing the simulation, compare your results with the theory:
Po ukończeniu symulacji, porównaj wyniki z teorią:
Object-oriented programming (OOP) organizes code into objects that combine data and behavior — like modeling real-world things in code.
Programowanie obiektowe (OOP) organizuje kod w obiekty, które łączą dane i zachowanie — jak modelowanie rzeczy z rzeczywistego świata w kodzie.
A class is a blueprint for creating objects — like a cookie cutter that defines the shape.
Klasa to szablon do tworzenia obiektów — jak foremka do ciastek określająca kształt.
An object (or instance) is a specific example created from a class — like an actual cookie made from the cutter.
Obiekt (lub instancja) to konkretny przykład utworzony z klasy — jak prawdziwe ciastko zrobione z foremki.
📘 Documentation / Dokumentacja:
Read sections: "Classes" and "Class Objects" / Przeczytaj sekcje: "Classes" i "Class Objects"
🎥 Video / Wideo:
Corey Schafer – OOP Tutorial Series (0:00–40:00)
Part 1 covers classes, instances, and methods / Część 1 omawia klasy, instancje i metody
🧩 Zadanie / Task:
Create a file classes.py:
Utwórz plik classes.py:
from typing import Any
# Simple class / Prosta klasa
class Dog:
"""Represents a dog / Reprezentuje psa"""
def __init__(self, name: str, age: int) -> None:
"""Initialize dog attributes / Zainicjuj atrybuty psa"""
self.name = name # instance variable / zmienna instancji
self.age = age
def bark(self) -> str:
"""Make the dog bark / Spraw, że pies szczeka"""
return f"{self.name} says Woof!"
def get_info(self) -> str:
"""Return dog information / Zwróć informacje o psie"""
return f"{self.name} is {self.age} years old"
# Create instances / Utwórz instancje
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
print(dog1.bark())
print(dog2.bark())
print(dog1.get_info())
print(dog2.get_info())
# Class with class variables / Klasa ze zmiennymi klasowymi
class Cat:
"""Represents a cat / Reprezentuje kota"""
species: str = "Felis catus" # class variable / zmienna klasowa
def __init__(self, name: str, color: str) -> None:
self.name = name # instance variable / zmienna instancji
self.color = color
def describe(self) -> str:
return f"{self.name} is a {self.color} cat of species {Cat.species}"
cat1 = Cat("Whiskers", "orange")
cat2 = Cat("Shadow", "black")
print(f"\n{cat1.describe()}")
print(f"{cat2.describe()}")
# Inheritance / Dziedziczenie
# Important: follows Liskov Substitution Principle (LSP) / Ważne: zgodne z zasadą podstawienia Liskov (LSP)
# LSP: You can write code for base class, and it will work with ANY subclass
# LSP: Możesz napisać kod dla klasy bazowej i będzie działał z KAŻDĄ podklasą
#
# Think of inheritance as: "A can be REPLACED by B", not "B is A"
# Myśl o dziedziczeniu jako: "A może być ZASTĄPIONE przez B", nie "B jest A"
# Example: "Employee can be replaced by PartTimeEmployee" ✓
# Przykład: "Employee może być zastąpiony przez PartTimeEmployee" ✓
# Read more / Czytaj więcej: https://en.wikipedia.org/wiki/Liskov_substitution_principle
class Employee:
"""
Base class for employees - defines the contract / Klasa bazowa dla pracowników - definiuje kontrakt
Subclasses MUST implement get_hours_per_week() / Podklasy MUSZĄ zaimplementować get_hours_per_week()
"""
def __init__(self, name: str, employee_id: str, hourly_rate: float) -> None:
self.name = name
self.employee_id = employee_id
self.hourly_rate = hourly_rate
def get_hours_per_week(self) -> int:
"""
Subclasses must implement this / Podklasy muszą to zaimplementować
This is the contract that all employees must fulfill / To jest kontrakt, który wszyscy pracownicy muszą spełnić
"""
raise NotImplementedError("Subclass must implement get_hours_per_week()")
def calculate_weekly_pay(self) -> float:
"""Calculate weekly payment / Oblicz tygodniowe wynagrodzenie"""
return self.hourly_rate * self.get_hours_per_week()
def get_info(self) -> str:
"""Get employee information / Uzyskaj informacje o pracowniku"""
return f"{self.name} (ID: {self.employee_id}), {self.get_hours_per_week()}h/week"
class PartTimeEmployee(Employee):
"""Part-time employee / Pracownik na część etatu"""
def __init__(self, name: str, employee_id: str, hourly_rate: float, hours: int) -> None:
super().__init__(name, employee_id, hourly_rate)
self.hours = hours
def get_hours_per_week(self) -> int:
"""Implementation: part-time has custom hours / Implementacja: część etatu ma własne godziny"""
return self.hours
class FullTimeEmployee(Employee):
"""Full-time employee with benefits / Pracownik pełnoetatowy z benefitami"""
def __init__(self, name: str, employee_id: str, hourly_rate: float, benefits: float) -> None:
super().__init__(name, employee_id, hourly_rate)
self.benefits = benefits
def get_hours_per_week(self) -> int:
"""Implementation: full-time is standard 40 hours / Implementacja: pełny etat to standardowe 40 godzin"""
return 40
def calculate_weekly_pay(self) -> float:
"""Override: add benefits to payment / Nadpisz: dodaj benefity do wypłaty"""
return super().calculate_weekly_pay() + self.benefits
# Create instances / Utwórz instancje
part_time = PartTimeEmployee("Alice", "E001", 25.0, 20)
full_time = FullTimeEmployee("Bob", "E002", 30.0, 200)
print(f"\n{part_time.get_info()}")
print(f"Weekly pay: ${part_time.calculate_weekly_pay():.2f}")
print(f"\n{full_time.get_info()}")
print(f"Weekly pay: ${full_time.calculate_weekly_pay():.2f}")
# ✅ LSP in action: code written for Employee works with ANY subclass
# ✅ LSP w akcji: kod napisany dla Employee działa z KAŻDĄ podklasą
def process_payroll(employees: list[Employee]) -> None:
"""
This function works with Employee and ALL its subclasses!
Ta funkcja działa z Employee i WSZYSTKIMI jego podklasami!
We wrote code for Employee, but it works with PartTimeEmployee and FullTimeEmployee too.
Napisaliśmy kod dla Employee, ale działa z PartTimeEmployee i FullTimeEmployee też.
"""
total = 0.0
print("\n--- Payroll Processing / Przetwarzanie wypłat ---")
for emp in employees:
pay = emp.calculate_weekly_pay()
total += pay
print(f"{emp.name}: ${pay:.2f}")
print(f"Total weekly payroll: ${total:.2f}")
# Works with any Employee subclass! / Działa z każdą podklasą Employee!
process_payroll([part_time, full_time])
print("\n" + "="*60)
print("❌ COUNTER-EXAMPLE 1: Bird/Penguin - VIOLATES LSP")
print("❌ KONTR-PRZYKŁAD 1: Ptak/Pingwin - NARUSZA LSP")
print("="*60)
# ❌ This is a classic WRONG example of inheritance / To jest klasyczny ZŁY przykład dziedziczenia
class Bird:
"""Bird that can fly / Ptak, który potrafi latać"""
def __init__(self, name: str) -> None:
self.name = name
def fly(self) -> str:
"""All birds can fly... right? / Wszystkie ptaki potrafią latać... prawda?"""
return f"{self.name} is flying!"
class Penguin(Bird):
"""Penguin is a bird, but... / Pingwin jest ptakiem, ale..."""
def fly(self) -> str:
# Problem: Penguins can't fly! / Problem: Pingwiny nie potrafią latać!
raise Exception(f"{self.name} can't fly - penguins are flightless!") # ❌ Breaks LSP!
# Code written for Bird / Kod napisany dla Bird
def make_bird_fly(bird: Bird) -> None:
"""
This function expects all Birds can fly / Ta funkcja oczekuje, że wszystkie Birds potrafią latać
But Penguin breaks this expectation! / Ale Penguin psuje to oczekiwanie!
"""
try:
result = bird.fly()
print(result)
except Exception as e:
print(f"✗ ERROR: {e}")
print("\nTesting regular Bird:")
sparrow = Bird("Sparrow")
make_bird_fly(sparrow) # ✓ Works / Działa
print("\nTesting Penguin:")
penguin = Penguin("Pingu")
make_bird_fly(penguin) # ✗ CRASHES! Violates LSP / WYWALA SIĘ! Narusza LSP
print("\n💡 Problem: 'Penguin IS-A Bird' in real life, but NOT in code!")
print("💡 Problem: 'Pingwin JEST ptakiem' w rzeczywistości, ale NIE w kodzie!")
print("The base class Bird promises fly() works, but Penguin breaks that promise.")
print("Klasa bazowa Bird obiecuje, że fly() działa, ale Penguin łamie tę obietnicę.")
print("\n" + "="*60)
print("❌ COUNTER-EXAMPLE 2: Rectangle/Square - VIOLATES LSP")
print("❌ KONTR-PRZYKŁAD 2: Prostokąt/Kwadrat - NARUSZA LSP")
print("="*60)
# ❌ This is a classic WRONG example of inheritance / To jest klasyczny ZŁY przykład dziedziczenia
class Rectangle:
"""Rectangle with independent width and height / Prostokąt z niezależnymi szerokością i wysokością"""
def __init__(self, width: float, height: float) -> None:
self.width = width
self.height = height
def set_width(self, width: float) -> None:
self.width = width
def set_height(self, height: float) -> None:
self.height = height
def get_area(self) -> float:
return self.width * self.height
class Square(Rectangle):
"""Square - seems like "Square IS-A Rectangle" but BREAKS substitution!"""
"""Kwadrat - wygląda jak "Kwadrat JEST prostokątem" ale PSUJE podstawienie!"""
def set_width(self, width: float) -> None:
# Square must keep width == height / Kwadrat musi trzymać width == height
self.width = width
self.height = width # ❌ This breaks LSP!
def set_height(self, height: float) -> None:
# Square must keep width == height / Kwadrat musi trzymać width == height
self.width = height # ❌ This breaks LSP!
self.height = height
# Code written for Rectangle / Kod napisany dla Rectangle
def test_rectangle(rect: Rectangle) -> None:
"""
This function expects Rectangle behavior / Ta funkcja oczekuje zachowania Rectangle
But Square breaks this expectation! / Ale Square psuje to oczekiwanie!
"""
rect.set_width(5)
rect.set_height(10)
area = rect.get_area()
print(f"Width: {rect.width}, Height: {rect.height}, Area: {area}")
# For Rectangle: we expect area = 50 (5 * 10)
# Dla Rectangle: oczekujemy area = 50 (5 * 10)
if area == 50:
print("✓ Works as expected / Działa zgodnie z oczekiwaniami")
else:
print(f"✗ BROKEN! Expected 50, got {area} / ZEPSUTE! Oczekiwano 50, otrzymano {area}")
print("\nTesting Rectangle:")
test_rectangle(Rectangle(0, 0)) # ✓ Works / Działa
print("\nTesting Square:")
test_rectangle(Square(0, 0)) # ✗ FAILS! Violates LSP / PSUJE SIĘ! Narusza LSP
print("\n💡 Key Lessons from BOTH counter-examples:")
print("💡 Kluczowe lekcje z OBUDWU kontr-przykładów:")
print("1. Bird/Penguin: Don't inherit if subclass can't fulfill base class contract (fly)")
print("1. Ptak/Pingwin: Nie dziedzicz, jeśli podklasa nie może spełnić kontraktu klasy bazowej (fly)")
print("2. Rectangle/Square: Don't inherit if subclass changes base class behavior")
print("2. Prostokąt/Kwadrat: Nie dziedzicz, jeśli podklasa zmienia zachowanie klasy bazowej")
print("\n✓ Use inheritance ONLY when: A can be REPLACED by B without breaking anything")
print("✓ Używaj dziedziczenia TYLKO gdy: A może być ZASTĄPIONE przez B bez psowania czegokolwiek")
# Special methods / Metody specjalne
class Book:
"""Represents a book / Reprezentuje książkę"""
def __init__(self, title: str, author: str, pages: int) -> None:
self.title = title
self.author = author
self.pages = pages
def __str__(self) -> str:
"""String representation for users / Reprezentacja stringowa dla użytkowników"""
return f"'{self.title}' by {self.author}"
def __repr__(self) -> str:
"""String representation for developers / Reprezentacja stringowa dla programistów"""
return f"Book(title='{self.title}', author='{self.author}', pages={self.pages})"
def __len__(self) -> int:
"""Return book length / Zwróć długość książki"""
return self.pages
def __eq__(self, other: Any) -> bool:
"""Check equality / Sprawdź równość"""
if not isinstance(other, Book):
return False
return self.title == other.title and self.author == other.author
book1 = Book("1984", "George Orwell", 328)
book2 = Book("1984", "George Orwell", 328)
print(f"\n{book1}") # uses __str__
print(f"{repr(book1)}") # uses __repr__
print(f"Pages: {len(book1)}") # uses __len__
print(f"Books equal? {book1 == book2}") # uses __eq__Run the program:
Uruchom program:
uv run python classes.pyExpected output / Oczekiwany rezultat:
Buddy says Woof!
Max says Woof!
Buddy is 3 years old
Max is 5 years old
Whiskers is a orange cat of species Felis catus
Shadow is a black cat of species Felis catus
Alice (ID: E001), 20h/week
Weekly pay: $500.00
Bob (ID: E002), 40h/week
Weekly pay: $1400.00
--- Payroll Processing / Przetwarzanie wypłat ---
Alice: $500.00
Bob: $1400.00
Total weekly payroll: $1900.00
============================================================
❌ COUNTER-EXAMPLE 1: Bird/Penguin - VIOLATES LSP
❌ KONTR-PRZYKŁAD 1: Ptak/Pingwin - NARUSZA LSP
============================================================
Testing regular Bird:
Sparrow is flying!
Testing Penguin:
✗ ERROR: Pingu can't fly - penguins are flightless!
💡 Problem: 'Penguin IS-A Bird' in real life, but NOT in code!
💡 Problem: 'Pingwin JEST ptakiem' w rzeczywistości, ale NIE w kodzie!
The base class Bird promises fly() works, but Penguin breaks that promise.
Klasa bazowa Bird obiecuje, że fly() działa, ale Penguin łamie tę obietnicę.
============================================================
❌ COUNTER-EXAMPLE 2: Rectangle/Square - VIOLATES LSP
❌ KONTR-PRZYKŁAD 2: Prostokąt/Kwadrat - NARUSZA LSP
============================================================
Testing Rectangle:
Width: 5, Height: 10, Area: 50.0
✓ Works as expected / Działa zgodnie z oczekiwaniami
Testing Square:
Width: 10, Height: 10, Area: 100.0
✗ BROKEN! Expected 50, got 100.0 / ZEPSUTE! Oczekiwano 50, otrzymano 100.0
💡 Key Lessons from BOTH counter-examples:
💡 Kluczowe lekcje z OBUÓW kontr-przykładów:
1. Bird/Penguin: Don't inherit if subclass can't fulfill base class contract (fly)
1. Ptak/Pingwin: Nie dziedzicz, jeśli podklasa nie może spełnić kontraktu klasy bazowej (fly)
2. Rectangle/Square: Don't inherit if subclass changes base class behavior
2. Prostokąt/Kwadrat: Nie dziedzicz, jeśli podklasa zmienia zachowanie klasy bazowej
✓ Use inheritance ONLY when: A can be REPLACED by B without breaking anything
✓ Używaj dziedziczenia TYLKO gdy: A może być ZASTĄPIONE przez B bez psowania czegokolwiek
'1984' by George Orwell
Book(title='1984', author='George Orwell', pages=328)
Pages: 328
Books equal? True
💾 Save to Git / Zapisz w Git:
git add classes.py
git commit -m "Add classes.py - OOP with LSP (Employee, Bird/Penguin, Rectangle/Square), special methods"Type hints add optional type information to your code — like labels on storage boxes telling you what's inside.
Type hints dodają opcjonalne informacje o typach do kodu — jak etykiety na pudłach mówiące, co jest w środku.
They don't change how code runs, but help catch errors early and make code more readable.
Nie zmieniają działania kodu, ale pomagają wcześniej wychwytywać błędy i czynią kod bardziej czytelnym.
dataclass is a decorator that automatically generates common methods for classes that mainly store data — reducing boilerplate code.
dataclass to dekorator, który automatycznie generuje powszechne metody dla klas przechowujących głównie dane — redukując kod kotłowy.
📘 Documentation / Dokumentacja:
Read sections: "Type Hints" and "Support for type hints" / Przeczytaj sekcje: "Type Hints" i "Support for type hints"
Read sections: "Module-level decorators" and "Post-init processing" / Przeczytaj sekcje: "Module-level decorators" i "Post-init processing"
🎥 Video / Wideo:
ArjanCodes – Python Type Hints Tutorial (0:00–20:00)
Covers type hints basics and benefits / Omawia podstawy type hints i korzyści
mCoding – Dataclasses in Python (0:00–15:00)
Explains dataclasses and when to use them / Wyjaśnia dataclass i kiedy ich używać
🧠 Dlaczego to ważne / Why it matters:
I've found that type hints make code self-documenting and help IDEs catch errors before you even run the program. Dataclasses eliminate repetitive boilerplate code for data-centric classes — something I really appreciate when working with data models.
Odkryłem, że type hints czynią kod samodokumentującym i pomagają IDE wychwytywać błędy zanim jeszcze uruchomisz program. Dataclass eliminuje powtarzalny kod kotłowy dla klas skoncentrowanych na danych — coś, co naprawdę doceniam podczas pracy z modelami danych.
🧩 Zadanie / Task:
Create a file type_hints.py:
Utwórz plik type_hints.py:
from typing import List, Dict, Optional, Union, Tuple
# Basic type hints / Podstawowe type hints
def greet(name: str) -> str:
"""Greet a person by name / Powitaj osobę po imieniu"""
return f"Hello, {name}!"
def add(a: int, b: int) -> int:
"""Add two integers / Dodaj dwie liczby całkowite"""
return a + b
def calculate_average(numbers: List[float]) -> float:
"""Calculate average of numbers / Oblicz średnią liczb"""
return sum(numbers) / len(numbers)
# Testing functions / Testowanie funkcji
print(greet("Alice"))
print(f"5 + 3 = {add(5, 3)}")
print(f"Average: {calculate_average([10.5, 20.0, 15.5])}")
# Optional type — can be value or None / Typ opcjonalny — może być wartością lub None
def find_user(user_id: int) -> Optional[Dict[str, str]]:
"""Find user by ID, return None if not found / Znajdź użytkownika po ID, zwróć None jeśli nie znaleziono"""
users = {
1: {"name": "Alice", "email": "alice@example.com"},
2: {"name": "Bob", "email": "bob@example.com"}
}
return users.get(user_id)
user = find_user(1)
if user:
print(f"\nFound user: {user['name']}")
else:
print("\nUser not found")
# Union type — can be multiple types / Typ Union — może być wieloma typami
def process_id(id_value: Union[int, str]) -> str:
"""Process ID as int or string / Przetwórz ID jako int lub string"""
return f"ID: {id_value}"
print(f"\n{process_id(123)}")
print(f"{process_id('ABC')}")
# Multiple return types / Wiele typów zwracanych
def get_coordinates() -> Tuple[float, float]:
"""Return x and y coordinates / Zwróć współrzędne x i y"""
return 10.5, 20.3
x, y = get_coordinates()
print(f"\nCoordinates: x={x}, y={y}")
# Type hints for variables / Type hints dla zmiennych
name: str = "Alice"
age: int = 25
scores: List[int] = [85, 90, 78]
person: Dict[str, Union[str, int]] = {"name": "Bob", "age": 30}
print(f"\n{name} ({age} years old)")
print(f"Scores: {scores}")
print(f"Person: {person}")Run the program:
Uruchom program:
uv run python type_hints.pyExpected output / Oczekiwany rezultat:
Hello, Alice!
5 + 3 = 8
Average: 15.333333333333334
Found user: Alice
ID: 123
ID: ABC
Coordinates: x=10.5, y=20.3
Alice (25 years old)
Scores: [85, 90, 78]
Person: {'name': 'Bob', 'age': 30}
💾 Save to Git / Zapisz w Git:
git add type_hints.py
git commit -m "Add type_hints.py - type hints with typing module"🧩 Zadanie / Task:
Create a file dataclass_demo.py:
Utwórz plik dataclass_demo.py:
from dataclasses import dataclass, field
from typing import List, Any
# Without dataclass — lots of boilerplate / Bez dataclass — dużo kodu kotłowego
class PersonOld:
def __init__(self, name: str, age: int, email: str) -> None:
self.name = name
self.age = age
self.email = email
def __repr__(self) -> str:
return f"PersonOld(name='{self.name}', age={self.age}, email='{self.email}')"
def __eq__(self, other: Any) -> bool:
if not isinstance(other, PersonOld):
return False
return (self.name == other.name and
self.age == other.age and
self.email == other.email)
# With dataclass — clean and concise / Z dataclass — czysto i zwięźle
@dataclass
class Person:
name: str
age: int
email: str
# Create instances / Utwórz instancje
person1 = Person("Alice", 25, "alice@example.com")
person2 = Person("Alice", 25, "alice@example.com")
person3 = Person("Bob", 30, "bob@example.com")
print(person1) # __repr__ generated automatically / __repr__ wygenerowany automatycznie
print(f"person1 == person2: {person1 == person2}") # __eq__ generated / __eq__ wygenerowany
print(f"person1 == person3: {person1 == person3}")
# Dataclass with default values / Dataclass z wartościami domyślnymi
@dataclass
class Product:
name: str
price: float
quantity: int = 0 # default value / wartość domyślna
in_stock: bool = True
product1 = Product("Laptop", 999.99, 5)
product2 = Product("Mouse", 29.99) # uses default quantity and in_stock
print(f"\n{product1}")
print(f"{product2}")
# Dataclass with field() / Dataclass z field()
@dataclass
class ShoppingCart:
customer: str
items: List[str] = field(default_factory=list) # mutable default / mutowalna wartość domyślna
def add_item(self, item: str) -> None:
self.items.append(item)
def total_items(self) -> int:
return len(self.items)
cart1 = ShoppingCart("Alice")
cart1.add_item("Book")
cart1.add_item("Pen")
cart2 = ShoppingCart("Bob")
cart2.add_item("Laptop")
print(f"\n{cart1.customer}'s cart: {cart1.items} ({cart1.total_items()} items)")
print(f"{cart2.customer}'s cart: {cart2.items} ({cart2.total_items()} items)")
# Frozen dataclass — immutable / Zamrożony dataclass — niezmienny
@dataclass(frozen=True)
class Point:
x: float
y: float
point = Point(10.5, 20.3)
print(f"\n{point}")
# point.x = 15 # This would raise an error! / To spowodowałoby błąd!
# Dataclass with post-init / Dataclass z post-init
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False) # not in __init__ / nie w __init__
def __post_init__(self) -> None:
"""Calculate area after initialization / Oblicz pole po inicjalizacji"""
self.area = self.width * self.height
rect = Rectangle(5, 3)
print(f"\n{rect}")
print(f"Rectangle area: {rect.area}")Run the program:
Uruchom program:
uv run python dataclass_demo.pyExpected output / Oczekiwany rezultat:
Person(name='Alice', age=25, email='alice@example.com')
person1 == person2: True
person1 == person3: False
Product(name='Laptop', price=999.99, quantity=5, in_stock=True)
Product(name='Mouse', price=29.99, quantity=0, in_stock=True)
Alice's cart: ['Book', 'Pen'] (2 items)
Bob's cart: ['Laptop'] (1 items)
Point(x=10.5, y=20.3)
Rectangle(width=5, height=3, area=15)
Rectangle area: 15
💾 Save to Git / Zapisz w Git:
git add dataclass_demo.py
git commit -m "Add dataclass_demo.py - dataclasses with field() and frozen"Pydantic is a popular alternative to dataclass with automatic data validation.
Pydantic to popularna alternatywa dla dataclass z automatyczną walidacją danych.
🧠 When to use what / Kiedy czego użyć:
| Feature / Cecha | dataclass |
pydantic |
|---|---|---|
| Built-in / Wbudowane | ✅ (Python 3.7+) | ❌ Need to install / Trzeba zainstalować |
| Data validation / Walidacja | ❌ Manual / Ręczna | ✅ Automatic / Automatyczna |
| Type coercion / Konwersja typów | ❌ | ✅ (e.g., "123" → 123) |
| JSON parsing / Parsowanie JSON | ❌ Manual / Ręczne | ✅ Built-in / Wbudowane |
| Best for / Najlepsze do | Simple data containers / Proste kontenery | APIs, config, validation / API, konfiguracja, walidacja |
📘 Documentation / Dokumentacja:
Read sections: "Models" and "Validators" / Przeczytaj sekcje: "Models" i "Validators"
🎥 Video / Wideo:
ArjanCodes – Pydantic Tutorial (0:00–15:00)
Covers Pydantic basics and validation / Omawia podstawy Pydantic i walidację
🧩 Zadanie / Task:
First, install Pydantic:
Najpierw zainstaluj Pydantic:
uv add pydanticCreate a file pydantic_demo.py:
Utwórz plik pydantic_demo.py:
from pydantic import BaseModel, Field, field_validator, ValidationError
from typing import List
# Basic Pydantic model / Podstawowy model Pydantic
class User(BaseModel):
"""User with automatic validation / Użytkownik z automatyczną walidacją"""
name: str
age: int
email: str
# Pydantic automatically validates! / Pydantic automatycznie waliduje!
try:
user1 = User(name="Alice", age=25, email="alice@example.com")
print(f"✓ Valid user: {user1}")
# This will fail validation / To nie przejdzie walidacji
user2 = User(name="Bob", age="invalid", email="bob@example.com")
except ValidationError as e:
print(f"\n✗ Validation error:\n{e}")
# Pydantic with field constraints / Pydantic z ograniczeniami pól
class Product(BaseModel):
"""Product with validation rules / Produkt z zasadami walidacji"""
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0, description="Price must be positive")
quantity: int = Field(ge=0, description="Quantity cannot be negative")
tags: List[str] = Field(default_factory=list)
product = Product(name="Laptop", price=999.99, quantity=5)
print(f"\n✓ Valid product: {product}")
try:
# This will fail: negative price / To nie przejdzie: ujemna cena
bad_product = Product(name="Mouse", price=-10, quantity=1)
except ValidationError as e:
print(f"\n✗ Validation error: {e.error_count()} error(s)")
for error in e.errors():
print(f" - {error['loc'][0]}: {error['msg']}")
# Custom validators / Własne walidatory
class Employee(BaseModel):
"""Employee with custom validation / Pracownik z własną walidacją"""
name: str
employee_id: str
salary: float
@field_validator('employee_id')
@classmethod
def validate_employee_id(cls, v: str) -> str:
"""Employee ID must start with 'E' / ID pracownika musi zaczynać się od 'E'"""
if not v.startswith('E'):
raise ValueError("Employee ID must start with 'E'")
if len(v) != 5:
raise ValueError("Employee ID must be 5 characters")
return v
emp = Employee(name="Alice", employee_id="E0001", salary=50000)
print(f"\n✓ Valid employee: {emp}")
try:
bad_emp = Employee(name="Bob", employee_id="123", salary=60000)
except ValidationError as e:
print(f"\n✗ Validation error: {e.errors()[0]['msg']}")
# JSON parsing - Pydantic advantage! / Parsowanie JSON - zaleta Pydantic!
import json
json_data = '{"name": "Charlie", "age": 30, "email": "charlie@example.com"}'
user_from_json = User.model_validate_json(json_data)
print(f"\n✓ Parsed from JSON: {user_from_json}")
# Convert back to JSON / Konwertuj z powrotem do JSON
print(f"✓ To JSON: {user_from_json.model_dump_json()}")
# Type coercion - Pydantic converts automatically! / Konwersja - Pydantic konwertuje automatycznie!
user_with_coercion = User(name="Dave", age="35", email="dave@example.com") # age is string but gets converted
print(f"\n✓ Type coercion: age was string '35', now int: {user_with_coercion.age} (type: {type(user_with_coercion.age).__name__})")Run the program:
Uruchom program:
uv run python pydantic_demo.pyExpected output / Oczekiwany rezultat:
✓ Valid user: name='Alice' age=25 email='alice@example.com'
✗ Validation error:
1 validation error for User
age
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='invalid', input_type=str]
✓ Valid product: name='Laptop' price=999.99 quantity=5 tags=[]
✗ Validation error: 1 error(s)
- price: Input should be greater than 0
✓ Valid employee: name='Alice' employee_id='E0001' salary=50000.0
✗ Validation error: Employee ID must start with 'E'
✓ Parsed from JSON: name='Charlie' age=30 email='charlie@example.com'
✓ To JSON: {"name":"Charlie","age":30,"email":"charlie@example.com"}
✓ Type coercion: age was string '35', now int: 35 (type: int)
💾 Save to Git / Zapisz w Git:
git add pydantic_demo.py
git commit -m "Add pydantic_demo.py - alternative to dataclass with validation"💡 Summary / Podsumowanie:
Use dataclass when / Użyj dataclass gdy:
- You need simple data containers / Potrzebujesz prostych kontenerów danych
- No external dependencies / Brak zewnętrznych zależności
- Basic structure without validation / Podstawowa struktura bez walidacji
Use pydantic when / Użyj pydantic gdy:
- You need automatic data validation / Potrzebujesz automatycznej walidacji
- Working with APIs or JSON / Pracujesz z API lub JSON
- Type coercion is helpful / Konwersja typów jest pomocna
- Building FastAPI applications / Budujesz aplikacje FastAPI
Good code isn't just code that works — it's code that's easy to read, maintain, and change.
Dobry kod to nie tylko kod, który działa — to kod łatwy do czytania, utrzymania i zmiany.
Don't Repeat Yourself means avoid duplicating code — if you write the same thing twice, extract it into a function or class.
Don't Repeat Yourself (Nie powtarzaj się) oznacza unikanie duplikacji kodu — jeśli piszesz to samo dwa razy, wyodrębnij to do funkcji lub klasy.
❌ Bad example / Zły przykład:
# Repeated code / Powtarzalny kod
print("Processing file1.txt...")
with open("file1.txt") as f:
data = f.read()
print(f"Read {len(data)} characters")
print("Processing file2.txt...")
with open("file2.txt") as f:
data = f.read()
print(f"Read {len(data)} characters")✅ Good example / Dobry przykład:
def process_file(filename: str) -> None:
print(f"Processing {filename}...")
with open(filename) as f:
data = f.read()
print(f"Read {len(data)} characters")
process_file("file1.txt")
process_file("file2.txt")Keep It Simple Stupid means write straightforward code — I've learned not to overcomplicate things just to look clever.
Keep It Simple Stupid (Zachowaj prostotę) oznacza pisanie prostego kodu — nauczyłem się nie komplikować rzeczy tylko po to, by wyglądać mądrze.
❌ Bad example / Zły przykład:
# Overcomplicated / Przekomplikowany
def is_even(n: int) -> bool:
return True if n % 2 == 0 else False if n % 2 != 0 else None✅ Good example / Dobry przykład:
def is_even(n: int) -> bool:
return n % 2 == 0The Zen of Python is a collection of 19 guiding principles for writing Python code.
Zen Pythona to zbiór 19 zasad kierunkowych pisania kodu w Pythonie.
You can view it by running:
Możesz go zobaczyć uruchamiając:
python -c "import this"Key principles / Kluczowe zasady:
- Beautiful is better than ugly / Piękne jest lepsze niż brzydkie
- Explicit is better than implicit / Jednoznaczne jest lepsze niż domniemane
- Simple is better than complex / Proste jest lepsze niż złożone
- Readability counts / Czytelność się liczy
- There should be one obvious way to do it / Powinien być jeden oczywisty sposób
📘 Read more / Przeczytaj więcej:
Read the full text and explanations / Przeczytaj pełny tekst i wyjaśnienia
🎥 Video / Wideo:
Real Python – The Zen of Python Explained (0:00–10:00)
Explains key principles with examples / Wyjaśnia kluczowe zasady z przykładami
🧩 Zadanie / Task:
Run the Zen of Python:
Uruchom Zen Pythona:
python -c "import this"Expected output / Oczekiwany rezultat:
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
...
PEP 8 is Python's style guide — like grammar rules for code.
PEP 8 to przewodnik stylu Pythona — jak zasady gramatyki dla kodu.
Common conventions / Powszechne konwencje:
-
Variables and functions:
snake_case(e.g.,user_name,calculate_total)Zmienne i funkcje:
snake_case(np.user_name,calculate_total) -
Classes:
PascalCase(e.g.,UserProfile,ShoppingCart)Klasy:
PascalCase(np.UserProfile,ShoppingCart) -
Constants:
UPPER_SNAKE_CASE(e.g.,MAX_SIZE,API_KEY)Stałe:
UPPER_SNAKE_CASE(np.MAX_SIZE,API_KEY) -
Private attributes:
_leading_underscore(e.g.,_internal_method)Atrybuty prywatne:
_wiodący_podkreślnik(np._internal_method)
📘 Read more / Przeczytaj więcej:
PEP 8 — Style Guide for Python Code
Read sections: "Naming Conventions" / Przeczytaj sekcję: "Naming Conventions"
Po tym bloku potrafisz / After this block you can:
- używać zmiennych i różnych typów danych (int, float, str, list, dict, tuple, set) / use variables and different data types (int, float, str, list, dict, tuple, set)
- kontrolować przepływ programu za pomocą if/elif/else, match-case i pętli for/while / control program flow with if/elif/else, match-case, and for/while loops
- sprawdzać None używając
is None/ check for None usingis None - tworzyć funkcje z type hints, parametrami i wartościami zwracanymi / create functions with type hints, parameters and return values
- definiować klasy z metodami i używać dziedziczenia zgodnie z LSP / define classes with methods and use inheritance following LSP
- rozumieć różnicę między "B is A" a "A can be replaced by B" / understand the difference between "B is A" and "A can be replaced by B"
- dodawać type hints do funkcji i klas dla lepszej czytelności / add type hints to functions and classes for better readability
- używać dataclass do uproszczenia klas przechowujących dane / use dataclass to simplify data-storing classes
- używać Pydantic jako alternatywy z automatyczną walidacją / use Pydantic as an alternative with automatic validation
- stosować zasady DRY, KISS i Zen of Python / apply DRY, KISS, and Zen of Python principles
- pisać czytelny kod zgodny z konwencjami PEP 8 / write readable code following PEP 8 conventions
- zarządzać kodem z Git i pushować na GitHub / manage code with Git and push to GitHub
| Termin | Znaczenie PL | Meaning EN |
|---|---|---|
| Variable | zmienna — pojemnik na dane | labeled container for data |
| Type | typ — rodzaj danych | kind of data |
| Function | funkcja — wielokrotnego użytku blok kodu | reusable code block |
| Class | klasa — szablon do tworzenia obiektów | blueprint for creating objects |
| Object / Instance | obiekt / instancja — konkretny przykład klasy | specific example of a class |
| Method | metoda — funkcja wewnątrz klasy | function inside a class |
| Attribute | atrybut — zmienna wewnątrz klasy | variable inside a class |
| Inheritance | dziedziczenie — klasa otrzymująca cechy od innej | class receiving features from another |
| Type hint | wskazówka typu — opcjonalna informacja o typie | optional type information |
| Dataclass | dataclass — klasa z automatycznymi metodami | class with automatic methods |
| Loop | pętla — powtarzanie kodu | code repetition |
| Condition | warunek — decyzja if/else | if/else decision |
| DRY | nie powtarzaj się | Don't Repeat Yourself |
| KISS | zachowaj prostotę | Keep It Simple |
Practice what you learned by building a simple task manager that combines functions, classes, type hints, and collections.
Przećwicz to, czego się nauczyłeś, budując prosty menedżer zadań, który łączy funkcje, klasy, type hints i kolekcje.
🧩 Zadanie / Task:
Create a file task_manager.py:
Utwórz plik task_manager.py:
from dataclasses import dataclass, field
from typing import List
from datetime import datetime
@dataclass
class Task:
title: str
completed: bool = False
created_at: datetime = field(default_factory=datetime.now)
def mark_complete(self) -> None:
self.completed = True
def __str__(self) -> str:
status = "✓" if self.completed else "○"
return f"{status} {self.title}"
class TaskManager:
def __init__(self):
self.tasks: List[Task] = []
def add_task(self, title: str) -> None:
task = Task(title)
self.tasks.append(task)
print(f"Added: {title}")
def complete_task(self, index: int) -> None:
if 0 <= index < len(self.tasks):
self.tasks[index].mark_complete()
print(f"Completed: {self.tasks[index].title}")
else:
print("Invalid task number") # a better way raise ValueError("Invalid task number") (will cover in a future lesson)
def show_tasks(self) -> None:
if not self.tasks:
print("No tasks yet!")
return
print("\nYour tasks:")
for i, task in enumerate(self.tasks, start=1):
print(f"{i}. {task}")
def get_stats(self) -> None:
total = len(self.tasks)
completed = sum(1 for task in self.tasks if task.completed)
pending = total - completed
print(f"\nStats: {completed}/{total} completed, {pending} pending")
# Demo / Demonstracja
def main() -> None:
manager = TaskManager()
# Add tasks / Dodaj zadania
manager.add_task("Learn Python basics")
manager.add_task("Write first program")
manager.add_task("Practice with exercises")
# Show tasks / Pokaż zadania
manager.show_tasks()
# Complete a task / Ukończ zadanie
manager.complete_task(0)
manager.complete_task(2)
# Show updated tasks / Pokaż zaktualizowane zadania
manager.show_tasks()
# Show statistics / Pokaż statystyki
manager.get_stats()
if __name__ == "__main__":
main()Run the program:
Uruchom program:
uv run python task_manager.pyExpected output / Oczekiwany rezultat:
Added: Learn Python basics
Added: Write first program
Added: Practice with exercises
Your tasks:
1. ○ Learn Python basics
2. ○ Write first program
3. ○ Practice with exercises
Completed: Learn Python basics
Completed: Practice with exercises
Your tasks:
1. ✓ Learn Python basics
2. ○ Write first program
3. ✓ Practice with exercises
Stats: 2/3 completed, 1 pending
💾 Save to Git / Zapisz w Git:
git add task_manager.py
git commit -m "Add task_manager.py - mini project combining all concepts"🎯 Challenge / Wyzwanie:
Try extending the task manager with these features:
Spróbuj rozszerzyć menedżer zadań o te funkcje:
- Add task priority (low, medium, high) / Dodaj priorytet zadania (niski, średni, wysoki)
- Add task due dates / Dodaj terminy zadań
- Save tasks to a file / Zapisuj zadania do pliku
- Load tasks from a file / Wczytuj zadania z pliku
After completing all exercises, push your work to GitHub to save it online and practice your Git skills!
Po ukończeniu wszystkich ćwiczeń, wypchnij pracę na GitHub aby zapisać ją online i przećwiczyć umiejętności Git!
🧩 Zadanie / Task:
Step 1: Review your commits / Krok 1: Przejrzyj commity
# See all your commits / Zobacz wszystkie commity
git log --oneline
# Check repository status / Sprawdź status repozytorium
git statusStep 2: Create GitHub repository (if not exists) / Krok 2: Utwórz repozytorium GitHub (jeśli nie istnieje)
If you don't have a remote repository yet:
Jeśli nie masz jeszcze zdalnego repozytorium:
# Using gh CLI (recommended) / Używając gh CLI (zalecane)
gh repo create python-learning --public --source=. --push
# Or manually create repo on GitHub.com and add remote / Lub ręcznie utwórz repo na GitHub.com i dodaj remote
git remote add origin https://github.com/your-username/python-learning.gitStep 3: Push all commits / Krok 3: Wypchnij wszystkie commity
# Push to main branch / Wypchnij na główną gałąź
git push -u origin main
# Or if you already set upstream / Lub jeśli już ustawiłeś upstream
git pushStep 4: Verify on GitHub / Krok 4: Zweryfikuj na GitHubie
-
Open your repository in browser:
Otwórz repozytorium w przeglądarce:
gh repo view --web
-
Check that all files are present:
Sprawdź czy wszystkie pliki są obecne:
01_basics/basics.py01_basics/collections.py01_basics/control_flow.py01_basics/loops.py01_basics/functions.py01_basics/game.py01_basics/classes.py01_basics/type_hints.py01_basics/dataclass_demo.py01_basics/task_manager.py01_basics/README.md
-
Review commit history to see all your progress!
Przejrzyj historię commitów aby zobaczyć cały postęp!
Expected result / Oczekiwany rezultat:
Your Phase 1 exercises are now safely stored on GitHub and accessible from anywhere!
Twoje ćwiczenia z Fazy 1 są teraz bezpiecznie zapisane na GitHubie i dostępne z każdego miejsca!