2-qism · Ma'lumot tuzilmalari va funksiyalar·08-bob
Funksiyalar: parametrlar, ko'rinish doirasi va closure'lar
def va return, argument turlari (pozitsion, kalit so'zli, *args, **kwargs, faqat-kalit va faqat-pozitsion), mutable default tuzog'i, LEGB qoidasi, closure, lambda, yuqori tartibli funksiyalar va functools.
Funksiya — dasturlashning asosiy qurilish bloki. Yaxshi yozilgan
funksiyalar kodni o'qiladigan, test qilinadigan va qayta ishlatiladigan
qiladi. Python'da funksiyalar yana bir xususiyatga ega: ular birinchi
darajali obyektlar — ularni o'zgaruvchiga saqlash, boshqa funksiyaga
berish, funksiyadan qaytarish mumkin. Bu dekoratorlar (14-bob),
callback'lar, key= parametrlari va ML'dagi ko'p naqshlarning asosi.
Ko'rinish doirasi — LEGB qoidasi, global va nonlocal. Funksiyalarning eng chalkash qismi; bobning 5 va 8-bo'limlari bilan bog'liq.
1. Asoslar#
def bmi(vazn_kg: float, boy_m: float) -> float:
"""Tana massasi indeksini hisoblaydi.
Args:
vazn_kg: vazn, kilogrammda.
boy_m: bo'y, metrda.
Returns:
BMI qiymati.
"""
return vazn_kg / boy_m ** 2
bmi(70, 1.75) # 22.857...def— funksiyani yaratadi va unga nom beradi (funksiya ham obyekt!).- Docstring —
help(bmi)va muharrir shu matnni ko'rsatadi. Yuqoridagi "Google uslubi" eng keng tarqalgan format. return— qiymat qaytaradi va funksiyani darhol tugatadi.returnsiz funksiyaNoneqaytaradi.
Bir nechta qiymat qaytarish#
def min_max(sonlar: list[float]) -> tuple[float, float]:
return min(sonlar), max(sonlar) # aslida bitta tuple qaytadi
kichik, katta = min_max([3, 1, 4])Erta qaytish (early return / guard clause)#
# ❌ Ichma-ich if'lar
def chegirma(user, summa):
if user is not None:
if user.is_active:
if summa > 100_000:
return summa * 0.1
return 0
# ✅ Erta qaytish — tekis, o'qiladigan
def chegirma(user, summa):
if user is None or not user.is_active:
return 0
if summa <= 100_000:
return 0
return summa * 0.12. Argumentlarni berish usullari#
def salomlash(ism, salom="Assalomu alaykum", belgi="!"):
return f"{salom}, {ism}{belgi}"
salomlash("Aziz") # pozitsion
salomlash("Aziz", "Salom") # pozitsion, ikkitasi
salomlash("Aziz", belgi=".") # kalit so'zli (keyword) — tartib muhim emas
salomlash(belgi="?", ism="Aziz") # hammasi kalit so'zliQoida: pozitsion argumentlar oldin, kalit so'zlilar keyin.
salomlash(ism="Aziz", "Salom") — SyntaxError.
*args va **kwargs#
def jami(*sonlar): # istalgancha pozitsion → tuple
return sum(sonlar)
jami(1, 2, 3) # 6
def log_event(nom, **maydonlar): # istalgancha kalit so'zli → dict
print(nom, maydonlar)
log_event("login", user_id=42, ip="10.0.0.1")
# login {'user_id': 42, 'ip': '10.0.0.1'}args va kwargs — shunchaki an'anaviy nomlar; muhimi * va **.
Chaqiruvda ochish#
params = {"w": 3, "h": 4}
def yuza(w, h): return w * h
yuza(**params) # yuza(w=3, h=4)
yuza(*[3, 4]) # yuza(3, 4)Bu juda ko'p ishlatiladi — masalan, konfiguratsiya lug'atini to'g'ridan-
to'g'ri funksiyaga uzatish: client.messages.create(**params).
Argumentlarni uzatish (forwarding)#
def wrapper(*args, **kwargs):
print("chaqirildi:", args, kwargs)
return asl_funksiya(*args, **kwargs) # hamma narsani o'zgarishsiz uzatadiBu naqsh dekoratorlarning asosi (14-bob).
3. Faqat-kalit va faqat-pozitsion parametrlar#
def f(a, b, /, c, *, d):
...
# ↑ ↑ ↑
# | | └── * dan keyingilar — FAQAT kalit so'z bilan
# | └──────── oddiy: ikkala usulda ham
# └────────────── / dan oldingilar — FAQAT pozitsion
f(1, 2, 3, d=4) # ✅
f(1, 2, c=3, d=4) # ✅
f(1, 2, 3, 4) # ❌ d faqat kalit so'z bilan
f(a=1, b=2, c=3, d=4) # ❌ a, b faqat pozitsionFaqat-kalit (*) — eng foydalisi. Bayroq (flag) va sozlama
parametrlarini majburan nomlash uchun:
def generate(prompt: str, *, max_tokens: int = 1024, temperature: float = 1.0, stream: bool = False):
...
generate("Salom", stream=True) # ✅ aniq
generate("Salom", 1024, 0.5, True) # ❌ TypeError — True nima ekani noaniq bo'lardiAnthropic, OpenAI SDK'lari va ko'p zamonaviy kutubxonalar aynan shunday
qilingan: client.messages.create(model=..., max_tokens=..., messages=...)
— hamma parametr faqat kalit so'z bilan.
4. Mutable default — Python'ning eng mashhur tuzog'i#
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] ← ?!Nega? Default qiymat funksiya yaratilganda bir marta hisoblanadi,
har chaqiruvda emas. items=[] — bitta ro'yxat obyekti, u funksiya
obyektida saqlanadi (add_item.__defaults__) va barcha chaqiruvlar uni
baham ko'radi.
To'g'ri usul — None va funksiya ichida yaratish:
def add_item(item, items: list | None = None) -> list:
if items is None:
items = []
items.append(item)
return items5. Ko'rinish doirasi (scope): LEGB qoidasi#
Python nomni qidirganda to'rtta qatlamni shu tartibda tekshiradi:
x = "global"
def tashqi():
x = "enclosing"
def ichki():
x = "local"
print(x) # local
ichki()
print(x) # enclosing
tashqi()
print(x) # globalTayinlash nomni lokal qiladi#
Eng chalkash qoida: agar funksiya ichida biror nomga qayerdadir tayinlash bo'lsa, u nom butun funksiya bo'ylab lokal hisoblanadi:
x = 10
def f():
print(x) # UnboundLocalError: cannot access local variable 'x'...
x = 5 # ← shu qator tufayli x butun f ichida lokal
f()global va nonlocal#
hisob = 0
def oshir():
global hisob # "modul darajasidagi hisob'ni o'zgartiraman"
hisob += 1global deyarli har doim yomon dizayn belgisi: funksiya ko'rinmas holatga
bog'lanib qoladi, testlash qiyinlashadi. Qiymatni parametr sifatida bering
va natijani qaytaring.
nonlocal — tashqi funksiyaning o'zgaruvchisini o'zgartirish uchun
(closure'larda kerak bo'ladi, pastda ko'ramiz).
Built-in nomlarni "yopib qo'ymang"#
list = [1, 2, 3] # endi list — sizning ro'yxatingiz, tip emas
list("abc") # TypeError: 'list' object is not callablelist, dict, str, id, type, input, sum, max, filter,
format kabi nomlarni o'zgaruvchi nomi sifatida ishlatmang. Jupyter'da bu
ayniqsa ko'p uchraydi va kernel'ni qayta ishga tushirmaguncha
saqlanib qoladi.
6. Funksiya — birinchi darajali obyekt#
def kvadrat(x):
return x * x
f = kvadrat # funksiyani o'zgaruvchiga saqlash (qavssiz!)
f(5) # 25
amallar = {"kv": kvadrat, "abs": abs}
amallar["kv"](3) # 9 — lug'atda saqlash
kvadrat.__name__ # 'kvadrat'
kvadrat.__doc__ # docstringf = kvadrat va f = kvadrat() farqi muhim: birinchisi funksiyaning
o'zi, ikkinchisi uni chaqirish natijasi.
Yuqori tartibli funksiyalar#
Funksiyani argument sifatida qabul qiladigan yoki qaytaradigan funksiya:
def apply_all(fns, x):
return [fn(x) for fn in fns]
apply_all([str.upper, str.lower, len], "Salom") # ['SALOM', 'salom', 5]Siz allaqachon ishlatgansiz: sorted(lst, key=len), max(d, key=d.get),
map(int, satrlar). ML'da: df.apply(fn), dataset.map(tokenize),
optimizer'ga loss_fn berish, callback'lar.
7. lambda — nomsiz kichik funksiya#
kvadrat = lambda x: x * x # ❌ — bunday qilmang, def yozing
sorted(juftlar, key=lambda p: p[1]) # ✅ — bir martalik, qisqalambda faqat bitta ifoda bo'lishi mumkin (returnsiz, ko'p
qatorsiz). Qoida: agar lambda'ga nom berayotgan bo'lsangiz yoki u bir
qatordan uzun bo'lsa — def yozing. def funksiyasining traceback'da
nomi ko'rinadi; lambda esa <lambda> deb chiqadi.
8. Closure — funksiya o'z muhitini eslab qoladi#
Ichki funksiya tashqi funksiyaning o'zgaruvchilarini "ushlab qoladi" — hatto tashqi funksiya tugaganidan keyin ham:
def make_multiplier(n):
def multiply(x):
return x * n # n — tashqi funksiyadan
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
double(5), triple(5) # (10, 15)double funksiyasi n = 2 ni o'zi bilan "olib yuradi". Bu closure.
Holatli closure: nonlocal#
def make_counter():
count = 0
def increment():
nonlocal count # tashqi count'ni qayta bog'lash uchun
count += 1
return count
return increment
c = make_counter()
c(); c()
print(c()) # 3Amaliy misol — API limitini kuzatuvchi:
import time
def make_rate_limiter(max_calls: int, per_seconds: float):
calls: list[float] = []
def allow() -> bool:
now = time.monotonic()
while calls and now - calls[0] > per_seconds: # eski chaqiruvlarni tashlash
calls.pop(0)
if len(calls) < max_calls:
calls.append(now)
return True
return False
return allow
allow = make_rate_limiter(max_calls=5, per_seconds=60)
if allow():
... # so'rov yuborish(Bu yerda calls mutatsiya qilinadi, qayta bog'lanmaydi — shuning uchun
nonlocal kerak emas.)
Kechikkan bog'lanish (late binding) tuzog'i#
fns = [lambda: i for i in range(3)]
[f() for f in fns] # [2, 2, 2] — [0, 1, 2] emas!Closure o'zgaruvchini ushlaydi, uning o'sha paytdagi qiymatini
emas. Hamma lambda bitta iga qaraydi va chaqirilganda u allaqachon 2.
Yechim — default argument bilan qiymatni "muzlatish":
fns = [lambda i=i: i for i in range(3)]
[f() for f in fns] # [0, 1, 2]9. Rekursiya#
Funksiya o'zini chaqirishi:
def faktorial(n: int) -> int:
if n <= 1: # tayanch holat (base case) — SHART
return 1
return n * faktorial(n - 1)Python'da rekursiya chuqurligi cheklangan (standart ≈ 1000) va dumli
rekursiya optimallashtirilmaydi. Chuqur rekursiya → RecursionError.
Daraxtsimon tuzilmalar (JSON, fayl tizimi, AST) uchun rekursiya tabiiy;
oddiy sikl bilan yechiladigan masalalarda siklni afzal ko'ring.
def json_depth(obj) -> int:
"""Ichma-ich JSON'ning chuqurligi."""
if isinstance(obj, dict):
return 1 + max((json_depth(v) for v in obj.values()), default=0)
if isinstance(obj, list):
return 1 + max((json_depth(v) for v in obj), default=0)
return 0
json_depth({"a": [{"b": 1}], "c": 2}) # 310. functools — funksiyalar uchun asboblar#
lru_cache / cache — natijalarni eslab qolish (memoization)#
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n: int) -> int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(100) # 354224848179261915075 — bir zumda (keshsiz — koinot umridan uzoq)
fib.cache_info() # CacheInfo(hits=98, misses=101, maxsize=None, currsize=101)@ — dekorator sintaksisi (14-bob). Kesh argumentlar bo'yicha ishlaydi,
shuning uchun argumentlar hashlanadigan bo'lishi kerak (ro'yxat berib
bo'lmaydi). Amalda: embedding'larni keshlash, qimmat hisob-kitoblar,
konfiguratsiyani bir marta o'qish.
partial — argumentlarning bir qismini "qotirish"#
from functools import partial
int_from_binary = partial(int, base=2)
int_from_binary("1010") # 10
def call_llm(prompt, *, model, effort): ...
quick = partial(call_llm, model="claude-opus-5-5", effort="low")
deep = partial(call_llm, model="claude-opus-5-5", effort="high")
quick("Bu sharh ijobiymi?")reduce — ketma-ketlikni bitta qiymatga yig'ish#
from functools import reduce
reduce(lambda a, b: a * b, [1, 2, 3, 4]) # 24Ko'pincha sum, max, math.prod, "".join yoki oddiy sikl
o'qilishi osonroq — reduceni faqat haqiqatan mos bo'lganda ishlating.
11. Yaxshi funksiya qanday bo'ladi#
| Tamoyil | Ma'nosi |
|---|---|
| Bitta vazifa | Funksiya nomini "va"siz ayta olishingiz kerak. load_and_clean_and_train — uchta funksiya |
| Aniq nom | Fe'l bilan: load_dataset, compute_loss, is_valid (bool uchun is_/has_) |
| Kam parametr | 4–5 dan ko'p bo'lsa — dataclass yoki konfiguratsiya obyekti (13-bob) |
| Yon ta'sirsiz (imkon qadar) | Argumentlarni o'zgartirmaydi, global holatga tegmaydi — natijani qaytaradi |
| Type hint'lar | Parametr va qaytish tiplari yozilgan (15-bob) |
| Docstring | Ommaviy funksiyalar uchun: nima qiladi, parametrlar, qaytaradi, qanday xato ko'taradi |
| Qisqa | Bir ekranga sig'adi (~20–40 qator); uzun bo'lsa — bo'laklarga ajrating |
Sof funksiya (pure function) — bir xil kirishga har doim bir xil
natija beradi va hech qanday yon ta'sirga ega emas. Bunday funksiyalarni
test qilish, parallellashtirish va keshlash oson. ML pipeline'ingizning
iloji boricha ko'p qismini sof funksiyalardan quring: clean_text,
tokenize, compute_metrics — hammasi sof bo'lishi mumkin.
Nima noto'g'ri ketishi mumkin#
| Holat | Natija | To'g'risi |
|---|---|---|
def f(x, lst=[]) | Chaqiruvlar orasida ro'yxat umumiy | lst=None, ichida [] |
def f(t=datetime.now()) | Vaqt bir marta hisoblanadi | t=None, ichida now() |
| Funksiya ichida global nomga tayinlash | UnboundLocalError | Parametr sifatida bering, natija qaytaring |
list = ..., sum = ... | Built-in yopiladi | Boshqa nom: items, total |
f = funksiya() funksiya o'rniga | Natija saqlanadi, funksiya emas | Qavssiz: f = funksiya |
| Sikldagi lambda'lar | Hammasi oxirgi qiymatni ko'radi | lambda i=i: ... |
return unutilgan | None qaytadi, keyinroq AttributeError: 'NoneType'... | Har shoxda return borligini tekshiring |
| Ko'p pozitsion bool argumentlar | f(x, True, False) — tushunarsiz | Faqat-kalit: def f(x, *, verbose=False) |
lru_cache + ro'yxat argument | TypeError: unhashable type | Tuple bering |
1. Tuzoqni toping
Bu funksiyada qanday xatolar bor? Tuzating:
from datetime import datetime
def log_message(msg, logs=[], ts=datetime.now()):
logs.append(f"[{ts:%H:%M:%S}] {msg}")
return logsMaslahatni ko'rsatish
Ikkita xato bor: mutable default va default'da datetime.now().
2. Faqat-kalit parametrlar bilan API wrapper
build_request(prompt, *, model, max_tokens=1024, temperature=None, system=None, stop=None)
funksiyasini yozing: API'ga yuboriladigan lug'atni qaytarsin (messages
maydoni [{"role": "user", "content": prompt}]), None bo'lgan
parametrlarni qo'shmasin. Pozitsion chaqirish (build_request("hi", "model"))
xato berishini tekshiring.
Maslahatni ko'rsatish
def chat(prompt: str, *, model: str = ..., max_tokens: int = 1024, system: str | None = None) -> dict. None bo'lgan maydonlarni dict comprehension bilan olib tashlang.
3. Closure bilan o'rtacha hisoblovchi
make_running_mean() funksiyasini yozing: u funksiya qaytarsin, har
chaqiruvda yangi qiymat qabul qilib, shu paytgacha kelgan barcha
qiymatlarning o'rtachasini qaytarsin. m = make_running_mean(); m(10); m(20)
→ 15.0. Bu training loop'da loss'ning o'rtachasini kuzatish uchun ishlatiladi.
Maslahatni ko'rsatish
Ichida total va count saqlang; nonlocal kerak bo'ladi, chunki int'lar qayta bog'lanadi.
4. Kompozitsiya va pipeline
compose(*fns) funksiyasini yozing: funksiyalarni o'ngdan chapga zanjir
qilsin (compose(f, g)(x) == f(g(x))). Keyin uni matnni tozalash
pipeline'ini yig'ish uchun ishlating: str.strip, str.lower, va 3-bobdagi
apostrof normalizatsiyasi. Bonus: pipeline(*fns) — chapdan o'ngga
tartibda.
- 01Funksiya — obyekt: saqlash, uzatish, qaytarish mumkin; f — funksiya, f() — natija.
- 02Pozitsion → kalit so'zli tartibi; *args → tuple, **kwargs → dict; chaqiruvda * va ** ochadi.
- 03* dan keyingi parametrlar faqat kalit so'z bilan — sozlama va bayroqlar uchun ideal.
- 04Default qiymat bir marta hisoblanadi: mutable default o'rniga None ishlating.
- 05LEGB: Local → Enclosing → Global → Built-in; tayinlash nomni butun funksiyada lokal qiladi.
- 06Closure tashqi o'zgaruvchini ushlaydi (qiymatini emas); nonlocal — qayta bog'lash uchun.
- 07functools: lru_cache — memoization, partial — argumentlarni qotirish.
- 08Yaxshi funksiya: bitta vazifa, aniq nom, kam parametr, yon ta'sirsiz, type hint va docstring bilan.
Mustahkamlash
Test va amaliy masalalar
Avval testni ishlang — har javobdan keyin izoh chiqadi. Keyin masalalarni shu yerning o'zida yechib, brauzerdagi haqiqiy Python bilan avtomatik tekshiring. Natijalaringiz kurs progressi sahifasida jamlanadi.
Bilimni tekshirish
Bob bo'yicha test
12 ta savol · har javobdan keyin to'g'ri yoki xatoligi va izoh ko'rsatiladi · oxirida natija foizda.
Amaliy masalalar
Kodni yozing va «Tekshirish»ni bosing (yoki Ctrl/⌘ + Enter). Kodingiz shu brauzerda saqlanib turadi.
01API so'rov yig'uvchi
build_request(prompt, *, model, max_tokens=1024, system=None, stop=None) — lug'at qaytarsin: {"model": ..., "max_tokens": ..., "messages": [{"role": "user", "content": prompt}]} + system va stop faqat None bo'lmasa. model va boshqalari faqat kalit so'z bilan berilsin.
02Running mean (closure)
make_running_mean() — funksiya qaytarsin: har chaqiruvda yangi son oladi va shu paytgacha kelgan barcha sonlarning o'rtachasini qaytaradi. m = make_running_mean(); m(10); m(20) → 15.0.
03Kompozitsiya
compose(*fns) — funksiyalarni o'ngdan chapga zanjir qilsin: compose(f, g)(x) == f(g(x)). Argumentsiz compose() — qiymatni o'zgarishsiz qaytaruvchi funksiya.