Kako vaši Python projekti rastu u veličini i kompleksnosti, brzo ćete otkriti da držanje svog koda u jednoj datoteci postaje neodrživo. To je trenutak kada moduli i paketi postaju neophodni. Ovi moćni koncepti omogućavaju vam da podelite kod u logičke celine, poboljšate ponovno korišćenje koda i kreirate strukturu koja je održiva i skalabilna. U ovom članku ćemo detaljno objasniti šta su moduli i paketi, kako ih kreirati i koristiti, i kako pomoću njih organizovati projekte na profesionalan način.
Šta su moduli?
Modul u Python-u je jednostavno Python datoteka sa ekstenzijom .py
koja sadrži definicije i izjave. Naziv modula je zapravo ime datoteke bez ekstenzije .py
. Na primer, datoteka matematika.py
je modul po imenu matematika
.
Kreiranje sopstvenog modula
Hajde da kreiramo jednostavan modul. Napravićemo datoteku matematika.py
sa nekoliko matematičkih funkcija:
# matematika.py
def saberi(a, b):
"""Sabira dva broja."""
return a + b
def oduzmi(a, b):
"""Oduzima drugi broj od prvog."""
return a - b
def pomnozi(a, b):
"""Množi dva broja."""
return a * b
def podeli(a, b):
"""Deli prvi broj drugim."""
if b == 0:
raise ValueError("Deljenje nulom nije dozvoljeno!")
return a / b
# Konstanta u modulu
PI = 3.14159265359
Importovanje i korišćenje modula
Kada kreiramo modul, možemo ga importovati u druge Python skripte kako bismo koristili njegove funkcije, klase i varijable. Postoji nekoliko načina da importujemo modul:
1. Import celog modula
import matematika
# Korišćenje funkcija iz modula
rezultat1 = matematika.saberi(5, 3)
rezultat2 = matematika.pomnozi(4, 6)
print(f"5 + 3 = {rezultat1}")
print(f"4 * 6 = {rezultat2}")
print(f"PI = {matematika.PI}")
2. Import konkretnih funkcija ili varijabli
from matematika import saberi, pomnozi, PI
# Direktno korišćenje importovanih funkcija
rezultat1 = saberi(5, 3)
rezultat2 = pomnozi(4, 6)
print(f"5 + 3 = {rezultat1}")
print(f"4 * 6 = {rezultat2}")
print(f"PI = {PI}")
3. Import svega iz modula (obično se ne preporučuje)
from matematika import *
# Direktno korišćenje svih funkcija i varijabli iz modula
rezultat1 = saberi(5, 3)
rezultat2 = pomnozi(4, 6)
print(f"5 + 3 = {rezultat1}")
print(f"4 * 6 = {rezultat2}")
print(f"PI = {PI}")
Napomena: Uvoz svega iz modula korišćenjem from modul import *
se obično ne preporučuje jer može dovesti do neočekivanih konflikata imena i čini teže razumevanje odakle određene funkcije dolaze.
Imenski prostori (Namespaces)
Kada importujete modul, Python kreira imenski prostor za taj modul. To sprečava konflikte imena između vašeg koda i koda u modulima koje importujete.
Na primer, ako imate funkciju saberi()
u vašem glavnom programu i importujete modul koji takođe ima funkciju saberi()
, one neće biti u konfliktu ako koristite metod import matematika
jer će funkcija iz modula biti dostupna kao matematika.saberi()
.
Dunder varijable u modulima
Python moduli sadrže posebne varijable koje počinju i završavaju se sa dvostrukim donjim crtama (dunder = double underscore). Na primer, __name__
je varijabla koja sadrži ime modula. Kada se skripta izvršava direktno (ne kao importovani modul), __name__
dobija vrednost "__main__"
.
Ovo nam omogućava da uključimo kod koji se izvršava samo kada se modul koristi kao samostalna skripta, ali ne i kada se importuje:
# matematika.py
# ... (prethodno definisane funkcije)
def test_funkcije():
"""Testira funkcije modula."""
print(f"2 + 3 = {saberi(2, 3)}")
print(f"5 - 2 = {oduzmi(5, 2)}")
print(f"4 * 6 = {pomnozi(4, 6)}")
print(f"8 / 4 = {podeli(8, 4)}")
print(f"PI = {PI}")
if __name__ == "__main__":
print("Pokretanje testova za matematika.py...")
test_funkcije()
Sada, ako pokrenemo matematika.py
direktno, testovi će se izvršiti. Ali ako importujemo modul u drugu skriptu, testovi se neće izvršiti.
Lociranje modula
Kada importujete modul, Python pretražuje nekoliko lokacija da ga pronađe:
- Trenutni direktorijum
- Liste direktorijuma u PYTHONPATH (ako je postavljena)
- Standardne biblioteke
- Liste direktorijuma za site-packages (gde su instalirani third-party paketi)
Možete videti listu putanja koje Python pretražuje koristeći varijablu sys.path
:
import sys
print(sys.path)
Šta su paketi?
Paket je kolekcija Python modula organizovanih u direktorijume. Paket je zapravo direktorijum koji sadrži Python module i opciono poddirektorijume koji mogu biti potpaketi. Ključna stvar koja čini direktorijum Python paketom je prisustvo posebne datoteke __init__.py
.
Kreiranje paketa
Hajde da kreiramo jednostavan paket. Prvo, napravićemo direktorijum matematicki_alati
i dodaćemo __init__.py
da ga označimo kao paket.
Struktura datoteka će izgledati ovako:
matematicki_alati/
__init__.py
osnovne_operacije.py
napredne_operacije.py
Sadržaj datoteka:
# matematicki_alati/__init__.py
# Ova datoteka može biti prazna, ili može inicijalizovati paket
print("Importovan matematicki_alati paket!")
# Možemo definisati koje module ili simbole paket izvozi
__all__ = ['osnovne_operacije', 'napredne_operacije']
python# matematicki_alati/osnovne_operacije.py
def saberi(a, b):
return a + b
def oduzmi(a, b):
return a - b
def pomnozi(a, b):
return a * b
def podeli(a, b):
if b == 0:
raise ValueError("Deljenje nulom nije dozvoljeno!")
return a / b
# matematicki_alati/napredne_operacije.py
import math
def stepenuj(osnova, exponent):
return osnova ** exponent
def kvadratni_koren(broj):
if broj < 0:
raise ValueError("Ne možemo izračunati kvadratni koren negativnog broja!")
return math.sqrt(broj)
def faktorijel(n):
if n < 0:
raise ValueError("Faktorijel nije definisan za negativne brojeve!")
if n == 0:
return 1
return n * faktorijel(n - 1)
Importovanje iz paketa
Postoji nekoliko načina za importovanje iz paketa:
1. Import specifičnog modula iz paketa
import matematicki_alati.osnovne_operacije
# Korišćenje
rezultat = matematicki_alati.osnovne_operacije.saberi(5, 3)
print(f"5 + 3 = {rezultat}")
2. Import specifične funkcije iz modula unutar paketa
from matematicki_alati.osnovne_operacije import saberi, pomnozi
# Korišćenje
rezultat1 = saberi(5, 3)
rezultat2 = pomnozi(4, 6)
print(f"5 + 3 = {rezultat1}")
print(f"4 * 6 = {rezultat2}")
3. Import modula sa alias-om
import matematicki_alati.osnovne_operacije as osnove
import matematicki_alati.napredne_operacije as napredne
# Korišćenje
rezultat1 = osnove.saberi(5, 3)
rezultat2 = napredne.kvadratni_koren(16)
print(f"5 + 3 = {rezultat1}")
print(f"√16 = {rezultat2}")
Uloga __init__.py
Datoteka __init__.py
ima nekoliko važnih uloga:
- Označava direktorijum kao Python paket: Python prepoznaje direktorijum kao paket samo ako sadrži
__init__.py
(u novijim verzijama Pythona, ovo pravilo je opušteno sa namespace paketima). - Inicijalizuje paket: Kod u
__init__.py
se izvršava kada se paket importuje. - Kontroliše koji moduli se importuju sa
from paket import *
: Lista__all__
u__init__.py
definiše koji moduli se importuju kada koristitefrom paket import *
. - Pojednostavljuje pristup simbolima: Možete importovati simbole iz modula u paketu u
__init__.py
kako bi bili direktno dostupni iz paketa.
Na primer, možemo modificirati __init__.py
tako da najvažnije funkcije postanu direktno dostupne:
# matematicki_alati/__init__.py
# Importujemo najčešće korišćene funkcije kako bi bile direktno dostupne
from .osnovne_operacije import saberi, oduzmi, pomnozi, podeli
from .napredne_operacije import stepenuj, kvadratni_koren
__all__ = ['osnovne_operacije', 'napredne_operacije', 'saberi', 'oduzmi',
'pomnozi', 'podeli', 'stepenuj', 'kvadratni_koren']
Sada možemo koristiti ove funkcije direktno:
from matematicki_alati import saberi, kvadratni_koren
print(saberi(5, 3)) # 8
print(kvadratni_koren(16)) # 4.0
Sub-paketi
Paketi mogu sadržati druge pakete, koji se nazivaju sub-paketi. Hajde da proširimo naš primer dodatnim sub-paketom za statističke funkcije:
matematicki_alati/
__init__.py
osnovne_operacije.py
napredne_operacije.py
statistika/
__init__.py
deskriptivna.py
verovatnoca.py
# matematicki_alati/statistika/__init__.py
from .deskriptivna import srednja_vrednost, medijana, standardna_devijacija
from .verovatnoca import normalna_raspodela
__all__ = ['deskriptivna', 'verovatnoca', 'srednja_vrednost',
'medijana', 'standardna_devijacija', 'normalna_raspodela']
# matematicki_alati/statistika/deskriptivna.py
def srednja_vrednost(podaci):
"""Izračunava aritmetičku srednju vrednost."""
return sum(podaci) / len(podaci)
def medijana(podaci):
"""Izračunava medijanu seta podataka."""
sortirani = sorted(podaci)
n = len(sortirani)
if n % 2 == 0:
return (sortirani[n//2 - 1] + sortirani[n//2]) / 2
else:
return sortirani[n//2]
def standardna_devijacija(podaci):
"""Izračunava standardnu devijaciju."""
mean = srednja_vrednost(podaci)
return (sum((x - mean) ** 2 for x in podaci) / len(podaci)) ** 0.5
# matematicki_alati/statistika/verovatnoca.py
import math
def normalna_raspodela(x, mu=0, sigma=1):
"""Izračunava gustinu verovatnoće normalne raspodele."""
return (1 / (sigma * math.sqrt(2 * math.pi))) * math.exp(-((x - mu) ** 2) / (2 * sigma ** 2))
Importovanje iz sub-paketa
Možemo importovati iz sub-paketa na sličan način kao iz paketa:
# Import specifičnog modula iz sub-paketa
import matematicki_alati.statistika.deskriptivna
# Import specifične funkcije iz modula u sub-paketu
from matematicki_alati.statistika.deskriptivna import srednja_vrednost, medijana
# Import sa alias-om
import matematicki_alati.statistika as stat
# Korišćenje
podaci = [1, 2, 3, 4, 5]
print(f"Srednja vrednost: {srednja_vrednost(podaci)}") # Direktno korišćenje
print(f"Medijana: {stat.medijana(podaci)}") # Korišćenje preko aliasa
Relativni importi
Kada radite u velikom projektu sa kompleksnom strukturom paketa, ponekad je korisno koristiti relativne importe. Ovi importi su relativni u odnosu na trenutni modul.
- Importe sa jednom tačkom
.
su relativni u odnosu na trenutni paket - Importe sa dve tačke
..
su relativni u odnosu na roditeljski paket
Na primer, unutar matematicki_alati/statistika/deskriptivna.py
, možemo importovati funkcije iz verovatnoca.py
na sledeći način:
# matematicki_alati/statistika/deskriptivna.py
# Relativni import iz istog paketa
from .verovatnoca import normalna_raspodela
# Relativni import iz roditeljskog paketa
from ..osnovne_operacije import saberi, podeli
Napomena: Relativni importi funkcionišu samo unutar paketa. Ne možete ih koristiti u skriptama koje se direktno izvršavaju.
Namespace paketi
U Python 3.3 i novijim verzijama, uvedeni su namespace paketi. Ovo su paketi koji ne zahtevaju __init__.py
. Omogućavaju razdvajanje paketa u više direktorijuma, što je korisno za velike projekte ili kada više paketa ima isti prefiks.
Namespace paketi se retko koriste u običnim projektima, ali je dobro znati da postoje.
Distribucija paketa
Kada kreirate korisne module ili pakete, možda ćete želeti da ih podelite sa drugima ili instalirati u različitim okruženjima. Python ima razvijen ekosistem za distribuciju paketa.
Kreiranje distribucije paketa
Osnovni način za distribuciju paketa je korišćenje setuptools
. Kreirajte datoteku setup.py
u korenu vašeg projekta:
# setup.py
from setuptools import setup, find_packages
setup(
name="matematicki_alati",
version="0.1",
packages=find_packages(),
install_requires=[], # Zavisnosti ako ih ima
author="Vaše Ime",
author_email="vas.email@example.com",
description="Paket matematičkih alata za različite operacije",
keywords="matematika, operacije, statistika",
url="https://github.com/vas-username/matematicki_alati",
)
Instalacija paketa
Možete instalirati lokalni paket u development modu:
pip install -e .
Ili napraviti distribucionu datoteku koju možete deliti:
python setup.py sdist
Ovo će kreirati dist/matematicki_alati-0.1.tar.gz
datoteku koju možete instalirati sa:
pip install matematicki_alati-0.1.tar.gz
PyPI – Python Package Index
Za javnu distribuciju, možete otpremiti svoj paket na PyPI (Python Package Index):
pip install twine
python setup.py sdist
twine upload dist/*
Nakon što je vaš paket na PyPI, bilo ko može instalirati vaš paket jednostavno sa:
pip install matematicki_alati
Najbolje prakse za organizaciju modula i paketa
1. Jasna struktura
Organizujte vaš kod u logičke celine. Na primer, za web aplikaciju:
moj_projekat/
moj_projekat/
__init__.py
modeli/
__init__.py
korisnik.py
proizvod.py
pogledi/
__init__.py
korisnik_pogledi.py
proizvod_pogledi.py
servisi/
__init__.py
email_servis.py
placanje_servis.py
tests/
__init__.py
test_modeli.py
test_pogledi.py
setup.py
README.md
2. “Jedan koncept, jedan modul”
Držite jedan logički koncept u jednom modulu. Na primer, sve što se tiče korisnika u korisnik.py
.
3. Izbjegavajte cirkularni import
Cirkularni importi (A importuje B, B importuje A) mogu izazvati probleme. Restrukturirajte kod da izbegnete ovo.
4. Koristite __all__
Definišite listu __all__
u svojim modulima da eksplicitno kontrolišete šta je javni API.
# moj_modul.py
__all__ = ['javna_funkcija', 'JavnaKlasa']
def javna_funkcija():
pass
def _privatna_funkcija(): # Konvencija: _ prefiks za "privatne" funkcije
pass
class JavnaKlasa:
pass
5. Dokumentujte svoj kod
Koristite docstringove da opišete šta moduli, klase i funkcije rade.
"""
Ovaj modul pruža osnovne matematičke operacije.
Funkcije:
- saberi(a, b): Sabira dva broja
- oduzmi(a, b): Oduzima drugi broj od prvog
"""
def saberi(a, b):
"""
Sabira dva broja i vraća rezultat.
Args:
a: Prvi broj
b: Drugi broj
Returns:
Zbir a i b
"""
return a + b
Korišćenje standardne biblioteke i third-party modula
Python dolazi sa bogatom standardnom bibliotekom koja sadrži module za mnoge uobičajene zadatke.
Primeri iz standardne biblioteke
# Rad sa vremenom
import datetime
sada = datetime.datetime.now()
print(f"Trenutno vreme: {sada}")
# Rad sa putanjama fajlova
import os.path
putanja = os.path.join("moj_direktorijum", "moj_fajl.txt")
print(f"Putanja fajla: {putanja}")
# Manipulacija stringovima
import re
pattern = r'\d+' # Jedan ili više cifara
tekst = "Imam 25 godina i 3 psa."
brojevi = re.findall(pattern, tekst) # ['25', '3']
print(f"Brojevi u tekstu: {brojevi}")
# Rad sa JSON podacima
import json
podaci = {"ime": "Ana", "godine": 25, "grad": "Beograd"}
json_string = json.dumps(podaci)
print(f"JSON string: {json_string}")
Instaliranje i korišćenje third-party modula
# Instalacija poznatih paketa
pip install requests # HTTP zahtevi
pip install numpy # Numerički proračuni
pip install pandas # Analiza podataka
# Korišćenje requests paketa za HTTP zahteve
import requests
response = requests.get("https://api.github.com")
if response.status_code == 200:
print("Uspešan zahtev!")
data = response.json()
print(f"GitHub API verzija: {data.get('current_user_url')}")
else:
print(f"Greška: Status kod {response.status_code}")
Virtual okruženja
Kada radite na više projekata, svaki sa svojim zavisnostima, korisno je koristiti virtualna okruženja. Ova okruženja izoluju zavisnosti jednog projekta od ostalih.
Kreiranje virtualnog okruženja
# Python 3.3+
python -m venv myenv
# Aktivacija u Windows
myenv\Scripts\activate
# Aktivacija u Linux/Mac
source myenv/bin/activate
Kada je virtualno okruženje aktivirano, svi paketi koje instalirate sa pip
biće instalirani samo u tom okruženju, a ne globalno.
Zaključak
Moduli i paketi su osnovni gradivni blokovi za organizovanje Python koda na održiv način. Pružaju mehanizme za razdvajanje koda u logičke celine, ponovno korišćenje funkcionalnosti i jednostavno deljenje koda sa drugima.
Razumevanje kako moduli i paketi funkcionišu i primjenjivanje najboljih praksi za njihovu organizaciju pomoći će vam da vaši projekti rastu na strukturiran i održiv način. Kako vaši projekti postaju složeniji, dobro organizovan kod postaje sve važniji za održavanje, testiranje i saradnju.
U sledećem članku, istražićemo rad sa datotekama i I/O operacije u Python-u, što je još jedna fundamentalna veština za praktično programiranje.