Last modified: October 28, 2024

This article is written in: 🇵🇱

Moduły i pakiety

W Pythonie moduły i pakiety są elementami umożliwiającymi organizację i strukturyzację kodu. Dzięki nim programy stają się bardziej czytelne, łatwiejsze w utrzymaniu i skalowalne. Ułatwiają one zarządzanie dużymi projektami oraz współpracę z innymi programistami. Zrozumienie tych elementów jest niezbędne dla efektywnego programowania i utrzymania czystego, modularnego kodu.

Moduły

Moduł to podstawowy sposób organizacji kodu. Jest to pojedynczy plik z rozszerzeniem .py, który może zawierać definicje funkcji, klas, zmiennych, a także kod wykonywalny. Moduły pozwalają na podzielenie kodu na mniejsze, logiczne części, co ułatwia zarządzanie i ponowne użycie kodu w innych projektach. Dzięki modułom możemy łatwo organizować nasz kod i unikać powielania funkcji, a także dzielić się nimi z innymi.

Przykład prostego modułu:

Utwórz plik o nazwie matematyka.py z następującą zawartością:

# matematyka.py

def dodaj(a, b):
    return a + b

def odejmij(a, b):
    return a - b

PI = 3.1415

W tym module:

Moduł matematyka.py jest teraz samodzielnym plikiem, który możemy zaimportować do innych skryptów, aby wykorzystać jego funkcje i zmienne.

Importowanie modułów

Aby skorzystać z funkcji i zmiennych z modułu matematyka.py w innym pliku, możemy go zaimportować za pomocą instrukcji import. Dzięki temu możemy używać wszystkich funkcji i zmiennych, które znajdują się w module, odwołując się do nich przez nazwę modułu.

Przykład użycia modułu:

# main.py

import matematyka

wynik = matematyka.dodaj(5, 7)
print(wynik)  # Output: 12

print(matematyka.PI)  # Output: 3.1415

W tym przykładzie:

Importowanie konkretnych elementów

Zamiast importować cały moduł, możemy importować tylko konkretne funkcje lub zmienne, co sprawia, że kod staje się bardziej zwięzły i czytelny. Dzięki temu nie musimy używać prefiksu modułu za każdym razem.

Przykład:

from matematyka import dodaj, PI

wynik = dodaj(10, 15)
print(wynik)  # Output: 25

print(PI)  # Output: 3.1415

W tym przypadku:

Aliasowanie modułów i funkcji

Czasami nazwa modułu lub funkcji może być długa lub może powodować konflikt nazw z innymi elementami w kodzie. W takich przypadkach możemy nadać alias (inną nazwę) modułowi lub funkcji, co ułatwia ich używanie.

Przykład aliasowania modułu:

import matematyka as mat

wynik = mat.odejmij(10, 5)
print(wynik)  # Output: 5

Tutaj:

Przykład aliasowania funkcji:

from matematyka import dodaj as d

wynik = d(3, 4)
print(wynik)  # Output: 7

W tym przypadku:

Korzyści z używania modułów

Pakiety

Pakiet to struktura, która umożliwia grupowanie powiązanych modułów w jednym katalogu. Dzięki pakietom możemy organizować kod na wyższym poziomie niż przy użyciu pojedynczych modułów, co jest szczególnie przydatne w większych projektach. Pakiet to po prostu folder, który zawiera moduły (pliki .py) oraz specjalny plik __init__.py. Obecność pliku __init__.py informuje Pythona, że dany folder powinien być traktowany jako pakiet, co pozwala na jego importowanie w innych częściach programu.

Struktura pakietu:

projekt/
├── kalkulator/
│   ├── __init__.py
│   ├── arytmetyka.py
│   └── geometry.py
└── main.py

Tworzenie pakietu

Aby utworzyć pakiet, wykonaj następujące kroki:

  1. Stwórz folder o nazwie pakietu - np. kalkulator, który będzie zawierał wszystkie moduły powiązane z tym pakietem.
  2. Utwórz plik __init__.py wewnątrz katalogu - może być pusty lub zawierać kod, który będzie wykonywany podczas importowania pakietu.
  3. Dodaj moduły (pliki .py) do katalogu pakietu - umieść tam pliki, takie jak arytmetyka.py czy geometry.py.

Przykład modułu w pakiecie:

W pliku arytmetyka.py możemy zdefiniować podstawowe operacje matematyczne:

# arytmetyka.py

def dodaj(a, b):
    return a + b

def odejmij(a, b):
    return a - b

W tym przykładzie:

Importowanie modułów z pakietu

Aby skorzystać z funkcji i klas zawartych w modułach pakietu, używamy notacji kropkowej (.). Możemy zaimportować cały moduł z pakietu, a następnie odwoływać się do jego zawartości za pomocą prefiksu modułu.

Przykład importowania modułu z pakietu:

# main.py

from kalkulator import arytmetyka

wynik = arytmetyka.dodaj(2, 3)
print(wynik)  # Output: 5

W tym przykładzie:

Importowanie konkretnych funkcji z modułu w pakiecie

Jeżeli chcemy zaimportować tylko wybrane funkcje lub klasy z modułu, możemy to zrobić bezpośrednio, co sprawia, że kod staje się bardziej zwięzły.

Przykład:

from kalkulator.arytmetyka import odejmij

wynik = odejmij(10, 4)
print(wynik)  # Output: 6

W tym przypadku:

Używanie pliku __init__.py

Plik __init__.py w pakiecie pełni kilka ważnych funkcji:

Przykład __init__.py:

# __init__.py

__all__ = ['arytmetyka', 'geometry']

Dzięki powyższemu ustawieniu:

Przykład użycia from pakiet import *:

# main.py

from kalkulator import *

wynik = arytmetyka.dodaj(5, 5)
print(wynik)  # Output: 10

W tym przypadku:

Importowanie modułów i pakietów

Importowanie modułów i pakietów w Pythonie jest kluczowe dla efektywnego zarządzania przestrzenią nazw i unikania duplikacji kodu. Oto różne metody importowania:

Zaimportowanie całego modułu

Pozwala to na dostęp do wszystkich funkcji, klas i zmiennych w module, używając notacji z kropką.

Przykład:

import os

current_directory = os.getcwd()
print(current_directory)

Zaimportowanie modułu z aliasem

Umożliwia korzystanie z krótszej nazwy modułu w kodzie.

Przykład:

import numpy as np

array = np.array([1, 2, 3])
print(array)

Zaimportowanie konkretnych funkcji z modułu

Importuje tylko określone funkcje lub klasy.

Przykład:

from math import sqrt, pi

print(sqrt(16))  # Output: 4.0
print(pi)        # Output: 3.141592653589793

Zaimportowanie funkcji z aliasem

Pozwala na zmianę nazwy importowanej funkcji.

Przykład:

from math import factorial as fac

print(fac(5))  # Output: 120

Zaimportowanie całej zawartości modułu

Importuje wszystkie publiczne elementy modułu.

Przykład:

from random import *

print(randint(1, 10))

Uwaga: Ta metoda może prowadzić do konfliktów nazw i jest ogólnie odradzana.

Wykonywanie kodu podczas importowania

W Pythonie kod znajdujący się poza definicjami funkcji i klas w module jest wykonywany natychmiast podczas importowania tego modułu. Oznacza to, że jeśli w module znajdują się instrukcje, które nie są umieszczone w funkcjach lub klasach, zostaną one wykonane, gdy tylko zaimportujemy moduł w innym pliku. Może to prowadzić do nieoczekiwanych efektów, takich jak wywoływanie funkcji lub drukowanie komunikatów, których nie planowaliśmy podczas importu.

Przykład problematycznego modułu:

Załóżmy, że mamy moduł misja.py z następującą zawartością:

# misja.py

def przygotuj_misje():
    print("Przygotowanie misji...")

def start_misji():
    print("Start misji!")

start_misji()  # Ta funkcja zostanie wywołana podczas importowania

W tym przykładzie:

Importowanie modułu:

Jeśli teraz zaimportujemy moduł misja w innym pliku:

import misja

To w konsoli zostanie wyświetlone:

Start misji!

Dzieje się tak dlatego, że Python wykonuje kod umieszczony poza definicjami funkcji natychmiast podczas importowania modułu. W tym przypadku nie chcieliśmy, aby funkcja start_misji() została uruchomiona podczas importu — powinna być wywoływana tylko wtedy, gdy rzeczywiście tego chcemy.

Rozwiązanie problemu

Aby uniknąć niechcianego wykonywania kodu podczas importowania, używamy konstrukcji:

if __name__ == "__main__":
    # Kod, który zostanie wykonany tylko przy bezpośrednim uruchomieniu modułu

Dzięki tej konstrukcji możemy określić, które fragmenty kodu mają być uruchamiane tylko wtedy, gdy moduł jest uruchamiany jako główny program, a nie podczas jego importowania.

Poprawiony moduł:

# misja.py

def przygotuj_misje():
    print("Przygotowanie misji...")

def start_misji():
    print("Start misji!")

if __name__ == "__main__":
    start_misji()

W tym przykładzie:

Wyjaśnienie działania if __name__ == "__main__":

Przykład zastosowania:

# narzedzia.py

def testuj_funkcje():
    print("Testowanie funkcji...")

if __name__ == "__main__":
    testuj_funkcje()

W tym module:

Uruchamianie narzedzia.py bezpośrednio:

Jeśli uruchomimy plik narzedzia.py bezpośrednio z linii poleceń:

python narzedzia.py

To na wyjściu zobaczymy:

Testowanie funkcji...

Ponieważ w tym przypadku __name__ przyjmuje wartość "__main__", kod w bloku if __name__ == "__main__": zostaje wykonany.

Importowanie narzedzia.py w innym pliku:

Jeśli zaimportujemy narzedzia.py w innym pliku:

# main.py

import narzedzia

Nie zobaczymy żadnego komunikatu w konsoli, ponieważ kod w bloku if __name__ == "__main__": nie zostanie wykonany — __name__ w module narzedzia nie ma wartości "__main__" podczas importu.

Importowanie relatywne i absolutne

W Pythonie możemy importować moduły i funkcje na dwa główne sposoby: za pomocą importów absolutnych oraz względnych. Obie metody mają swoje zastosowania i są wybierane w zależności od struktury projektu oraz potrzeb programisty.

Importy absolutne

Importy absolutne odwołują się do modułów, używając ich pełnej ścieżki, poczynając od katalogu głównego projektu. Oznacza to, że określamy dokładnie, skąd ma pochodzić importowany moduł, bez względu na to, w którym miejscu struktury katalogów znajduje się plik, który wykonuje import.

Przykład importu absolutnego:

from kalkulator.arytmetyka import dodaj

W tym przykładzie: - from kalkulator.arytmetyka import dodaj wskazuje, że funkcja dodaj powinna zostać zaimportowana z modułu arytmetyka, który znajduje się w pakiecie kalkulator. - Ścieżka do modułu arytmetyka jest podana w sposób pełny, poczynając od katalogu kalkulator.

Zaletą importów absolutnych jest to, że są one jasne i czytelne – widać dokładnie, z którego modułu pochodzi import. Dzięki temu łatwiej jest zrozumieć strukturę projektu, zwłaszcza gdy jest on duży i zawiera wiele pakietów.

Importy względne

Importy względne odwołują się do modułów na podstawie ich położenia względem bieżącego modułu, w którym wykonujemy import. Są przydatne, gdy chcemy odwołać się do modułów, które znajdują się w tym samym pakiecie lub w pokrewnych pakietach. W przypadku importów względnych używamy kropek (.) do wskazania położenia modułów:

Przykład importu względnego:

from .arytmetyka import dodaj  # Importuje funkcję dodaj z modułu arytmetyka w bieżącym pakiecie

W tym przykładzie: - from .arytmetyka import dodaj wskazuje, że funkcja dodaj jest importowana z modułu arytmetyka, który znajduje się w tym samym pakiecie co bieżący plik. - Użycie jednej kropki (.) sprawia, że odwołujemy się do modułu arytmetyka w ramach tego samego pakietu.

Importy względne są szczególnie przydatne w dużych projektach, w których moduły są zorganizowane w hierarchiczne struktury pakietów. Pozwalają one na łatwe przenoszenie całych pakietów bez konieczności zmieniania ścieżek importu, co ułatwia refaktoryzację kodu.

Uwaga: Ograniczenia importów względnych

Przykład sytuacji, kiedy import względny może nie działać:

Jeżeli mamy strukturę katalogów:

projekt/
├── kalkulator/
│   ├── __init__.py
│   ├── arytmetyka.py
│   └── geometry.py
└── main.py

i w pliku arytmetyka.py znajduje się import względny:

# arytmetyka.py

from .geometry import oblicz_pole

To ten import będzie działał, gdy arytmetyka.py zostanie zaimportowany jako część pakietu kalkulator, na przykład w main.py:

# main.py

from kalkulator import arytmetyka

Ale jeśli spróbujemy uruchomić arytmetyka.py bezpośrednio:

python kalkulator/arytmetyka.py

otrzymamy błąd, ponieważ importy względne nie działają, gdy plik jest uruchamiany bezpośrednio.

Kiedy używać importów absolutnych, a kiedy względnych?

Importy absolutne:

Importy względne:

Dobre praktyki

Praktyczne przykłady

Tworzenie pakietu analiza_danych

W poniższym przykładzie tworzymy pakiet analiza_danych, który będzie zawierał moduły odpowiedzialne za różne etapy analizy danych.

Struktura pakietu:

projekt/
├── analiza_danych/
│   ├── __init__.py
│   ├── wczytywanie.py
│   ├── przetwarzanie.py
│   └── wizualizacja.py
└── main.py

Moduł wczytywanie.py

def wczytaj_csv(sciezka):
    # Kod do wczytywania pliku CSV
    pass

W module wczytywanie.py znajduje się funkcja wczytaj_csv, która przyjmuje argument sciezka, czyli ścieżkę do pliku CSV, który chcemy wczytać. Ta funkcja mogłaby używać bibliotek takich jak pandas, aby załadować dane z pliku do DataFrame'a:

import pandas as pd

def wczytaj_csv(sciezka):
    return pd.read_csv(sciezka)

Powyższy kod wczytuje dane z pliku CSV i zwraca je w postaci DataFrame, co jest bardzo wygodne przy dalszej analizie danych.

Moduł przetwarzanie.py

def filtruj_dane(dane):
    # Kod do filtrowania danych
    pass

Funkcja filtruj_dane w module przetwarzanie.py zajmuje się przetwarzaniem danych. Jako argument przyjmuje dane, które mogą być np. DataFrame'em wczytanym wcześniej. Przykładowo, możemy chcieć odfiltrować dane na podstawie pewnych warunków:

def filtruj_dane(dane, kolumna, wartosc):
    return dane[dane[kolumna] > wartosc]

W tym przykładzie funkcja filtruje wiersze, w których wartość w danej kolumnie jest większa od podanej wartości. Dzięki temu możemy łatwo wybrać tylko te dane, które spełniają określone kryteria.

Moduł wizualizacja.py

def wykres_liniowy(dane):
    # Kod do tworzenia wykresu liniowego
    pass

Moduł wizualizacja.py zawiera funkcję wykres_liniowy, która tworzy wykresy na podstawie dostarczonych danych. Zwykle takie wykresy są generowane za pomocą bibliotek takich jak matplotlib lub seaborn:

import matplotlib.pyplot as plt

def wykres_liniowy(dane, x, y):
    plt.figure(figsize=(10, 6))
    plt.plot(dane[x], dane[y], marker='o')
    plt.title('Wykres Liniowy')
    plt.xlabel(x)
    plt.ylabel(y)
    plt.grid(True)
    plt.show()

W powyższym kodzie funkcja wykres_liniowy rysuje wykres, gdzie x i y to nazwy kolumn, które mają być użyte do osi X i Y. Wykres zostanie wyświetlony za pomocą matplotlib.

Plik __init__.py

from .wczytywanie import wczytaj_csv
from .przetwarzanie import filtruj_dane
from .wizualizacja import wykres_liniowy

__all__ = ['wczytaj_csv', 'filtruj_dane', 'wykres_liniowy']

Plik __init__.py pozwala na łatwy dostęp do funkcji pakietu analiza_danych z zewnątrz. Importując te funkcje wewnątrz __init__.py, możemy je potem bezpośrednio zaimportować w innych plikach, takich jak main.py. __all__ definiuje listę funkcji, które są dostępne do importu z poziomu pakietu.

Użycie pakietu w main.py

from analiza_danych import wczytaj_csv, filtruj_dane, wykres_liniowy

dane = wczytaj_csv('dane.csv')
dane_przefiltrowane = filtruj_dane(dane, 'kolumna', 100)
wykres_liniowy(dane_przefiltrowane, 'czas', 'wartość')

W pliku main.py importujemy funkcje wczytaj_csv, filtruj_dane i wykres_liniowy z pakietu analiza_danych i używamy ich do przetworzenia danych.

  1. Funkcja wczytaj_csv ładuje dane z pliku dane.csv do DataFrame'a.
  2. Funkcja filtruj_dane przefiltrowuje dane, wybierając tylko te, gdzie wartość w kolumnie 'kolumna' jest większa niż 100.
  3. Funkcja wykres_liniowy generuje wykres liniowy, pokazując zależność między kolumnami 'czas' i 'wartość'.

Spis Treści

  1. Moduły
    1. Przykład prostego modułu:
    2. Importowanie modułów
    3. Przykład użycia modułu:
    4. Importowanie konkretnych elementów
    5. Przykład:
    6. Aliasowanie modułów i funkcji
    7. Przykład aliasowania modułu:
    8. Przykład aliasowania funkcji:
    9. Korzyści z używania modułów
  2. Pakiety
    1. Struktura pakietu:
    2. Tworzenie pakietu
    3. Przykład modułu w pakiecie:
    4. Importowanie modułów z pakietu
    5. Przykład importowania modułu z pakietu:
    6. Importowanie konkretnych funkcji z modułu w pakiecie
    7. Przykład:
    8. Używanie pliku __init__.py
    9. Przykład __init__.py:
    10. Przykład użycia from pakiet import *:
  3. Importowanie modułów i pakietów
    1. Zaimportowanie całego modułu
    2. Zaimportowanie modułu z aliasem
    3. Zaimportowanie konkretnych funkcji z modułu
    4. Zaimportowanie funkcji z aliasem
    5. Zaimportowanie całej zawartości modułu
  4. Wykonywanie kodu podczas importowania
    1. Przykład problematycznego modułu:
    2. Importowanie modułu:
    3. Rozwiązanie problemu
    4. Poprawiony moduł:
    5. Wyjaśnienie działania if __name__ == "__main__":
    6. Przykład zastosowania:
    7. Uruchamianie narzedzia.py bezpośrednio:
    8. Importowanie narzedzia.py w innym pliku:
  5. Importowanie relatywne i absolutne
    1. Importy absolutne
    2. Importy względne
    3. Uwaga: Ograniczenia importów względnych
    4. Przykład sytuacji, kiedy import względny może nie działać:
    5. Kiedy używać importów absolutnych, a kiedy względnych?
  6. Dobre praktyki
  7. Praktyczne przykłady
    1. Tworzenie pakietu analiza_danych
    2. Moduł wczytywanie.py
    3. Moduł przetwarzanie.py
    4. Moduł wizualizacja.py
    5. Plik __init__.py
    6. Użycie pakietu w main.py