Priprema za Python backend intervju

Python pitanja i odgovori za intervju 2026

Fokusirana kolekcija Python pitanja za backend intervjue. Koristite svaki odgovor kao strukturu za usmenu vežbu: koncept, primer i trade-offs.

Započni Python mock intervjuKreditna kartica nije potrebna. Dostupna je 1 besplatna sesija.
Vežba tehničkog intervjua na engleskomRežim u kome kandidati kojima engleski nije maternji mogu da vežbaju tehničke intervjue.

Core Python Data Model

1Koje ugrađene tipove podataka ima Python i koji od njih su promenljivi?

Python ima skalarne tipove kao što su NoneType, bool, int, float i complex; tekstualne i binarne tipove kao što su str, bytes i bytearray; sekvencijalne tipove kao što su list, tuple i range; i kolekcijske tipove kao što su dict, set i frozenset. Važna distinkcija za intervju je promenljivost (mutability): list, dict, set i bytearray se mogu menjati na mestu (in-place), dok su brojevi, bool, str, bytes, tuple, range, frozenset i None nepromenljivi. Korisna nijansa je to što su sopstvene reference na elemente torke (tuple) fiksirane, ali unutrašnje stanje promenljivog objekta na koji torka referencira i dalje može da se menja.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

2Kako se list, tuple, set i dict razlikuju u realnom kodu?

Tip `list` je uređena promenljiva sekvenca, pa je pogodan za redove poslova, prikupljene rezultate i uređene podatke koji se menjaju. Tip `tuple` je uređen ali nepromenljiv, pa je koristan za fiksne zapise, povratne vrednosti funkcija i vrednosti koje ne bi trebalo menjati. Tip `set` čuva jedinstvene hešabilne objekte i idealan je za provere pripadnosti skupu ili uklanjanje duplikata. Tip `dict` mapira hešabilne ključeve na vrednosti i uobičajena je struktura za indekse, pretrage i podatke nalik JSON-u. U modernom Python-u, `dict` čuva redosled umetanja, ali mu je primarna svrha i dalje pristup zasnovan na ključu.

items = ["a", "b", "a"]
unique = set(items)
index = {"user_id": 42}
point = (10, 20)
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

3Koja je razlika između `is` i `==`?

`==` proverava da li su dva objekta jednaka po vrednosti, obično putem metode `__eq__`. `is` proverava da li dve reference pokazuju na potpuno isti objekat u memoriji. U produkcionom kodu, `is` bi obično trebalo rezervisati za provere jedinstvenih instanci (singleton) kao što je `value is None`, zatim `value is True` samo kada je identitet zaista važan, ili za kontrolne objekte (sentinel objects). Ne treba se oslanjati na detalje implementacije kao što su keširanje malih celih brojeva ili interniranje niski; oni mogu učiniti da `is` prividno radi u primerima, iako predstavlja pogrešan semantički izbor.

value = None

if value is None:
    print("missing")
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

4Šta je hešabilan objekat (hashable object) i zašto je to važno?

Hešabilan objekat ima heš vrednost koja ostaje stabilna tokom njegovog životnog veka i ponašanje poređenja jednakosti koje je dosledno sa tom heš vrednošću. Mogućnost heširanja je važna jer ključevi u `dict` i elementi u `set` zavise od nje. Većina ugrađenih nepromenljivih tipova je hešabilna, ali ne postaju automatski hešabilni svi objekti unutar nepromenljivog kontejnera: `tuple` koji sadrži `list` nije hešabilan. Na intervjuima povežite ovo sa dizajnom: ako se objekat koristi kao ključ, polja koja učestvuju u `__eq__` ili `__hash__` ne smeju se menjati nakon umetanja, jer u suprotnom objekat može postati nedostupan u `dict` ili `set` kolekciji.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

5Kako funkcioniše isecanje sekvenci (slicing), uključujući negativne indekse i korake?

Isecanje sekvence `sequence[start:stop:step]` uključuje `start`, isključuje `stop` i pomera se za `step`. Granice koje nedostaju određuju se na osnovu smera koraka. Negativni indeksi broje elemente unazad od kraja, pa je `s[-1]` poslednji element. Negativan korak se kreće zdesna nalevo; `s[::-1]` kreira obrnutu sekvencu za mnoge tipove sekvenci. Kompromis je u tome što isecanje često stvara novi objekat, pa iako je izražajno, nije uvek memorijski i računski besplatno za velike količine podataka.

text = "interview"

print(text[1:5])
print(text[::-1])
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Functions and Scope

6Kako Python prosleđuje argumente funkcijama?

Python prosleđuje reference na objekte putem dodele, što se često naziva call by sharing. Funkcija dobija lokalna imena parametara povezana sa istim objektima koje je prosledio pozivalac. Ako je objekat promenljiv, funkcija ga može izmeniti i pozivalac će videti tu promenu. Ako funkcija ponovo dodeli vrednost imenu parametra, menja se samo lokalno vezivanje. Dobar odgovor na intervjuu razlikuje promenu na samom objektu od lokalnog ponovnog vezivanja: items.append(...) ili items.clear() menja listu pozivaoca, dok items = [] unutar funkcije samo usmerava lokalno ime na novu listu.

def add_item(items):
    items.append("new")

def replace_items(items):
    items = []

values = []
add_item(values)
replace_items(values)

print(values)  # ["new"]
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

7Zašto su promenljivi podrazumevani argumenti opasni?

Podrazumevane vrednosti argumenata se evaluiraju jednom, prilikom definisanja funkcije, a ne pri svakom njenom pozivu. Ako je podrazumevana vrednost lista ili rečnik, svi pozivi mogu deliti isti objekat i slučajno prenositi ili curenjem izmeniti stanje. Bezbedan obrazac je `def f(items=None):`, a zatim unutar funkcije kreirati `items = []` kada je `items` jednako `None`. Postoje retki slučajevi kada je deljeno stanje namerno, kao što je jednostavan keš, ali to tada treba da bude eksplicitno i dokumentovano jer je neočekivano za one koji čitaju kod.

def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

print(add_tag("python"))
print(add_tag("backend"))  # Same list reused

def safe_add_tag(tag, tags=None):
    tags = [] if tags is None else tags
    tags.append(tag)
    return tags
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

8Čemu služe *args i **kwargs?

*args prikuplja dodatne pozicione argumente u torku, dok **kwargs prikuplja dodatne imenovane argumente u rečnik. Korisni su za omotače, dekoratore, fleksibilne API-je i prosleđivanje poziva. Na mestu poziva, *iterable raspakuje pozicione vrednosti, a **mapping raspakuje imenovane vrednosti. Kompromis je u čitljivosti: eksplicitni parametri su bolji kada funkcija ima stabilan javni ugovor, dok su args i kwargs bolji na granicama gde su prosleđivanje ili kompatibilnost važni.

def call_api(path, *args, **kwargs):
    print(path, args, kwargs)

call_api("/users", 1, 2, timeout=5)

values = [1, 2]
options = {"timeout": 5}
call_api("/users", *values, **options)
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

9Kako Python razrešava imena promenljivih pomoću LEGB (Local, Enclosing, Global, Built-in) pravila?

Python traži imena u opsezima vidljivosti: Local, Enclosing, Global i Builtins. Čitanje imena prati taj redosled. Dodeljivanje vrednosti imenu unutar funkcije obično kreira lokalno vezivanje, zbog čega kod može izazvati `UnboundLocalError` ako čita ime pre lokalne dodele. Ključna reč `global` preusmerava dodelu na opseg modula, dok `nonlocal` preusmerava dodelu na obuhvatajući opseg funkcije. U kvalitetnom produkcionom kodu, obe ključne reči treba koristiti umereno jer čine tok stanja manje očiglednim.

total = 0

def add(value):
    global total
    total += value

def counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        return count

    return increment
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

10Šta je zatvorenje (closure) i šta je kasno vezivanje (late binding)?

Zatvorenje (closure) je funkcija koja zadržava pristup promenljivama iz svog obuhvatajućeg opsega vidljivosti i nakon što je spoljna funkcija završila izvršavanje i vratila vrednost. Ovo je korisno za dekoratore, fabrike povratnih poziva i konfigurisane funkcije. Kasno vezivanje (late binding) znači da se vrednost slobodne promenljive traži tek u trenutku poziva unutrašnje funkcije, a ne u trenutku njenog kreiranja. Zbog toga lambda funkcije kreirane u petlji mogu videti samo konačnu vrednost promenljive iz petlje. Trenutna vrednost se može sačuvati korišćenjem podrazumevanog argumenta, na primer `lambda i=i: i`, ili upotrebom male fabričke funkcije.

callbacks = []

for i in range(3):
    callbacks.append(lambda: i)

print([callback() for callback in callbacks])  # [2, 2, 2]

callbacks = []
for i in range(3):
    callbacks.append(lambda i=i: i)

print([callback() for callback in callbacks])  # [0, 1, 2]
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

OOP, Iteration and Resource Management

11Kako se promenljive klase razlikuju od promenljivih instance?

Promenljiva klase se čuva na objektu klase i dele je sve instance, osim ako je instanca ne zaseni sopstvenim atributom. Promenljiva instance se čuva na konkretnom objektu i obično se dodeljuje kao `self.name` u `__init__`. Nepromenljive konstante na nivou klase su uglavnom bezbedne, ali promenljivi atributi klase predstavljaju čest izvor grešaka kada se slučajno koriste kao stanje pojedinačne instance. Na intervjuu pomenite redosled pretrage: Python prvo proverava instancu, a zatim klasu i njene pretke.

class Cart:
    items = []

first = Cart()
second = Cart()

first.items.append("book")
print(second.items)  # ["book"]

class SafeCart:
    def __init__(self):
        self.items = []
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

12Kako funkcionišu nasleđivanje, MRO (Method Resolution Order) i super()?

Python razrešava atribute putem redosleda razrešavanja metoda (MRO), koji se može videti preko `ClassName.__mro__`. Kod jednostrukog nasleđivanja postupak je jednostavan, ali kod višestrukog nasleđivanja Python koristi algoritam C3 linearizacije kako bi formirao konzistentan redosled. Poziv `super()` ne znači doslovno „pozovi mog roditelja”, već „pozovi sledeću implementaciju u MRO redosledu”. To omogućava kooperativno višestruko nasleđivanje kada metode prihvataju kompatibilne argumente i kada svaka klasa ispravno poziva `super()`.

class A:
    def save(self):
        print("A")

class B(A):
    def save(self):
        print("B")
        super().save()

class C(A):
    def save(self):
        print("C")
        super().save()

class D(B, C):
    pass

D().save()  # B, C, A
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

13Koja je razlika između iterable, iterator i generator objekata u Python-u?

Objekat koji se može iterirati (iterable) jeste svaki objekat koji može da vrati iterator pomoću funkcije iter(), kao što su lista ili fajl objekat. Iterator je objekat sa stanjem koji implementira metodu __next__ i izaziva izuzetak StopIteration kada se iscrpi. Generator je praktičan način za kreiranje iteratora pomoću ključne reči yield ili generatorskog izraza. Generatori su memorijski efikasni jer vrednosti proizvode lenjo (on-demand), ali se po pravilu mogu iskoristiti samo jednom i ne podržavaju indeksiranje kao lista.

def read_lines(path):
    with open(path, encoding="utf-8") as file:
        for line in file:
            yield line.strip()
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

14Šta je menadžer konteksta i zašto je with koristan?

Menadžer konteksta definiše ponašanje pri ulasku i izlasku pomoću metoda __enter__ i __exit__, ili se može kreirati pomoću contextlib.contextmanager. Naredba with čini upravljanje resursima eksplicitnim i pouzdanim: datoteke se zatvaraju, katanci (locks) se oslobađaju, a transakcije mogu da se potvrde ili vrate unazad čak i kada dođe do izuzetka. Metod __exit__ prima informacije o izuzetku i može ga potisnuti vraćanjem vrednosti true, mada potiskivanje grešaka treba koristiti oprezno.

from contextlib import contextmanager

with open("data.txt", encoding="utf-8") as file:
    data = file.read()

@contextmanager
def temporary_setting():
    print("enter")
    try:
        yield
    finally:
        print("exit")
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

15Koja je razlika između plitkog kopiranja (shallow copy) i dubokog kopiranja (deep copy)?

Plitko kopiranje kreira novi spoljni kontejner, ali zadržava reference na iste ugnježdene objekte. Duboko kopiranje rekurzivno kopira ugnježdene objekte kada je to moguće. Za jednostavne ravne liste plitko kopiranje je često dovoljno. Kod ugnježdenih promenljivih struktura, plitko kopiranje i dalje deli stanje, što može dovesti do neočekivanog ponašanja. Duboko kopiranje je računski zahtevnije i može dovesti do grešaka TypeError ili PicklingError za resurse kao što su otvorene datoteke, soketi, sesije baze podataka ili objekti čije ponašanje zavisi od identiteta instance.

from copy import copy, deepcopy

original = [[1], [2]]
shallow = copy(original)
deep = deepcopy(original)

original[0].append(99)

print(shallow)  # [[1, 99], [2]]
print(deep)     # [[1], [2]]
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Runtime, Async and Production Code

16Šta je GIL (Global Interpreter Lock) i kako utiče na backend kôd?

U standardnom CPython-u, Global Interpreter Lock dozvoljava samo jednoj niti da izvršava Python bajtkôd u datom trenutku. To znači da niti obično ne ubrzavaju Python kôd koji je ograničen procesorom (CPU-bound). Niti su i dalje korisne za poslove ograničene ulazom/izlazom (I/O-bound) jer se GIL oslobađa tokom čekanja na mnoge blokirajuće operacije. Za poslove koji intenzivno koriste procesor, koristite multiprocessing, nativne ekstenzije, vektorizovane nativne biblioteke ili zasebne servise. Za intervjue u 2026. godini, takođe pomenite CPython verzije bez globalnog zaključavanja (free-threaded builds) u Python 3.13+: one čine GIL opcionim za kompatibilan kôd, ali i dalje zahtevaju testiranje zavisnosti i performansi.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

17Kada treba izabrati niti, procese ili asyncio?

Koristite niti kada imate blokirajući ulaz/izlaz (I/O) ili biblioteke koje nude samo sinhroni API (Application Programming Interface). Koristite procese za rad zahtevan po procesor (CPU-bound), izolaciju ili sprečavanje rušenja celog sistema, uz prihvatanje troškova serijalizacije i zauzeća memorije. Koristite asyncio kada imate mnogo konkurentnih I/O operacija i biblioteke kompatibilne sa asinhronim kodom, kao što su asinhroni drajveri za baze podataka ili HTTP klijenti. Praktičan izbor zavisi i od radnog okvira i zavisnosti: mešanje asinhronog koda sa blokirajućim pozivima može pogoršati performanse.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom

18Za šta je async/await pogodan u Python veb-servisima?

async i await omogućavaju kooperativnu konkurentnost. Korutina prepušta kontrolu petlji događaja (event loop) dok čeka ulazno/izlazne (I/O) operacije, omogućavajući drugim zadacima da se izvršavaju. Ovo je izuzetno korisno za API-je koji čekaju na baze podataka, HTTP pozive, redove poruka ili sokete. To ne ubrzava automatski kod koji intenzivno koristi procesor (CPU-bound); procesorski zahtevan rad blokira petlju događaja osim ako se ne prebaci na radnu nit, proces ili nativni kod. U radnom okviru FastAPI, standardna def krajnja tačka se izvršava u skupu niti, dok blokirajući pozivi unutar async def mogu blokirati petlju događaja; async pomaže samo kada celokupna putanja poziva koristi neblokirajući asinhroni I/O.

import asyncio

async def load_user():
    await asyncio.sleep(0.1)
    return {"id": 1}

async def main():
    user, settings = await asyncio.gather(
        load_user(),
        load_user(),
    )
    return user, settings
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

19Kako bi trebalo rukovati izuzecima u produkcionom Python kodu?

Uhvatite najspecifičniji izuzetak kojim možete upravljati i uhvatite ga na sloju koji može da se oporavi, doda kontekst ili ga prevede u domensku grešku. Izbegavajte prazan `except` i tiho ignorisanje grešaka jer to prikriva gubitak podataka i operativne probleme. Koristite `raise` za ponovno izazivanje istog izuzetka i `raise NewError(...) from exc` kako biste sačuvali uzročni kontekst. Kvalitetan produkcioni kod takođe beleži logove na granicama sistema, vraća odgovarajuće HTTP greške i održava poruke o izuzecima korisnim bez otkrivanja osetljivih podataka.

try:
    result = parse_payload(payload)
except ValueError as exc:
    raise InvalidRequest("Invalid payload") from exc
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

20Kako biste strukturirali i testirali produkcioni Python servis?

Odvojite domensku logiku od koda radnog okvira i infrastrukture, tako da se osnovno ponašanje može testirati bez pokretanja servera ili baze podataka. Koristite konfiguraciju iz promenljivih okruženja ili tipizirana podešavanja, namensko evidentiranje (logging) umesto funkcije `print`, ubacivanje zavisnosti (dependency injection) na granicama sistema i eksplicitna vremenska ograničenja (timeouts) za mrežne pozive. Za testove kombinujte jedinične testove za čistu logiku, integracione testove za bazu podataka ili spoljne granice i nekoliko end-to-end provera za kritične tokove. Simulirajte spoljne sisteme na jasnim granicama, ali testirajte uočljivo ponašanje umesto internih detalja implementacije.

Pokušajte da odgovorite na ovo pitanje sa AI trenerom