aidoc.uz

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.

▶ Video dars· Corey Schafer· 20:58· ingliz tilida
Python Tutorial: Variable Scope - Understanding the LEGB rule and global/nonlocal statements

Ko'rinish doirasi — LEGB qoidasi, global va nonlocal. Funksiyalarning eng chalkash qismi; bobning 5 va 8-bo'limlari bilan bog'liq.

YouTube'da ochish ↗

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 funksiya None qaytaradi.

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.1

2. 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'zli

Qoida: 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 uzatadi

Bu 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 pozitsion

Faqat-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'lardi

Anthropic, 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 items

5. Ko'rinish doirasi (scope): LEGB qoidasi#

Python nomni qidirganda to'rtta qatlamni shu tartibda tekshiradi:

L — Local
Joriy funksiya ichida yaratilgan nomlar
1
E — Enclosing
Tashqi (o'rab turgan) funksiyalarning nomlari
2
G — Global
Modul (fayl) darajasidagi nomlar
3
B — Built-in
len, print, range, ValueError...
4
x = "global"
 
def tashqi():
    x = "enclosing"
    def ichki():
        x = "local"
        print(x)          # local
    ichki()
    print(x)              # enclosing
 
tashqi()
print(x)                  # global

Tayinlash 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 += 1

global 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 callable

list, 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__              # docstring

f = 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, qisqa

lambda 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())                    # 3

Amaliy 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})   # 3

10. 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])   # 24

Ko'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#

TamoyilMa'nosi
Bitta vazifaFunksiya nomini "va"siz ayta olishingiz kerak. load_and_clean_and_train — uchta funksiya
Aniq nomFe'l bilan: load_dataset, compute_loss, is_valid (bool uchun is_/has_)
Kam parametr4–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'larParametr va qaytish tiplari yozilgan (15-bob)
DocstringOmmaviy funksiyalar uchun: nima qiladi, parametrlar, qaytaradi, qanday xato ko'taradi
QisqaBir 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#

HolatNatijaTo'g'risi
def f(x, lst=[])Chaqiruvlar orasida ro'yxat umumiylst=None, ichida []
def f(t=datetime.now())Vaqt bir marta hisoblanadit=None, ichida now()
Funksiya ichida global nomga tayinlashUnboundLocalErrorParametr sifatida bering, natija qaytaring
list = ..., sum = ...Built-in yopiladiBoshqa nom: items, total
f = funksiya() funksiya o'rnigaNatija saqlanadi, funksiya emasQavssiz: f = funksiya
Sikldagi lambda'larHammasi oxirgi qiymatni ko'radilambda i=i: ...
return unutilganNone qaytadi, keyinroq AttributeError: 'NoneType'...Har shoxda return borligini tekshiring
Ko'p pozitsion bool argumentlarf(x, True, False) — tushunarsizFaqat-kalit: def f(x, *, verbose=False)
lru_cache + ro'yxat argumentTypeError: unhashable typeTuple bering
Amaliy topshiriq

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 logs
Maslahatni ko'rsatish

Ikkita xato bor: mutable default va default'da datetime.now().

Amaliy topshiriq

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.

Amaliy topshiriq

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.

Amaliy topshiriq

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.

Yodda saqlang
  • 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.

Test: topshirilmaganMasalalar: 0/3

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.