functools — partial, reduce, lru_cache

~20 daq · Python · Ilg'or mavzular

Muammodan boshlaylik

def narx_formatla(summa, valyuta, ajratuvchi, kasr):
    ...

narx_formatla(1500000, "so'm", " ", 0)
narx_formatla(250000, "so'm", " ", 0)
narx_formatla(85000, "so'm", " ", 0)

Har safar bir xil uchta argument. Boshqa muammo: kursni API dan oladigan funksiya bir xil valyuta uchun soniyasiga yuzlab marta chaqiriladi — har safar tarmoq so'rovi. Va ro'yxatni bitta qiymatga "yig'ish" kerak — sum va max dan boshqa amal bilan.

functools — funksiyalar bilan ishlaydigan funksiyalar moduli.


Hayotiy o'xshatma

Ustaxona moslamalari. partial — oldindan sozlangan asbob: kerakli razmerdagi uchni bir marta mahkamlab qo'yasiz va keyin faqat ishlatasiz. lru_cache — ustaning daftari: bir marta hisoblangan o'lchamni qayta hisoblamaydi, yozib qo'ygan. reduce — bir qop yong'oqni bittalab chaqib, bitta kosaga yig'ish.


partial — argumentlarni oldindan to'ldirish

from functools import partial

def narx_formatla(summa, valyuta="so'm", ajratuvchi=" ", kasr=0):
    satr = f"{summa:,.{kasr}f}".replace(",", ajratuvchi)
    return f"{satr} {valyuta}"

somda = partial(narx_formatla, valyuta="so'm", ajratuvchi=" ")
dollarda = partial(narx_formatla, valyuta="
quot;, ajratuvchi=",", kasr=2) print(somda(1500000)) # 1 500 000 so'm print(dollarda(1234.5)) # 1,234.50 $

partial(f, ...) — ba'zi argumentlari "mahkamlangan" yangi funksiya.

Foydali joylar

from functools import partial

# int ni boshqa sanoq tizimi bilan
ikkilikdan = partial(int, base=2)
print(ikkilikdan("1010"))          # 10

# map, sorted, callback larga uzatish
def kopaytir(x, k):
    return x * k

print(list(map(partial(kopaytir, k=3), [1, 2, 3])))   # [3, 6, 9]
# tugma bosilganda argument bilan chaqiriladigan funksiya
tugma.on_click(partial(ochir, mahsulot_id=42))

Lambda ham shunday qila oladi (lambda x: kopaytir(x, k=3)), lekin partial ning nomi va argumentlari ko'rinib turadi va sikldagi lambda tuzog'iga (20-dars) tushmaydi.


reduce — ketma-ketlikni bitta qiymatga yig'ish

from functools import reduce

sonlar = [1, 2, 3, 4, 5]
kopaytma = reduce(lambda a, b: a * b, sonlar)
print(kopaytma)       # 120

Qanday ishlaydi:

reduce(f, [1, 2, 3, 4, 5])
  f(1, 2) = 2
  f(2, 3) = 6
  f(6, 4) = 24
  f(24, 5) = 120

Boshlang'ich qiymat bilan (bo'sh ro'yxatda xato bermasligi uchun):

reduce(lambda a, b: a * b, [], 1)     # 1

Ichma-ich lug'atdan qiymat olish

from functools import reduce

malumot = {"user": {"manzil": {"shahar": "Toshkent"}}}
yol = ["user", "manzil", "shahar"]

print(reduce(lambda d, kalit: d[kalit], yol, malumot))   # Toshkent

[!TIP] reduce ko'pincha o'qish uchun qiyin. Ko'p vazifalar uchun aniqroq muqobillar bor: sum(), max(), min(), any(), all(), "".join(), math.prod() (ko'paytma), yoki oddiy for sikl. reduce ni haqiqatan "zanjirli yig'ish" kerak bo'lganda ishlating.

import math
math.prod([1, 2, 3, 4, 5])    # 120 — reduce siz

lru_cache — natijalarni eslab qolish

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(200))      # bir zumda (22-dars — memoization)

Bir xil argumentlar bilan qayta chaqirilsa — funksiya bajarilmaydi, natija keshdan olinadi.

maxsize — kesh hajmi

@lru_cache(maxsize=128)
def kurs(valyuta):
    print(f"API ga so'rov: {valyuta}")
    return api_dan_kurs_ol(valyuta)

kurs("USD")      # API ga so'rov: USD
kurs("USD")      # — keshdan, so'rov yo'q
kurs("EUR")      # API ga so'rov: EUR

print(kurs.cache_info())     # CacheInfo(hits=1, misses=2, maxsize=128, currsize=2)
kurs.cache_clear()           # keshni tozalash

LRU — "Least Recently Used": kesh to'lganda eng uzoq vaqt ishlatilmagan natija chiqarib tashlanadi. maxsize=None — chegarasiz (xotira o'sishi mumkin).

@cache (Python 3.9+) — @lru_cache(maxsize=None) ning qisqa shakli.

Qachon ishlatish va ehtiyot choralari

✅ Mos:

  • Pure funksiyalar — natija faqat argumentlarga bog'liq (rekursiv hisoblar, parsing, og'ir matematik hisoblar)
  • Bir xil argumentlar ko'p takrorlanadi

❌ Mos emas:

  • Natija vaqt bilan o'zgaradi — datetime.now(), bazadagi o'zgaruvchan ma'lumot, valyuta kursi (kesh hech qachon yangilanmaydi — cache_clear() yoki vaqt bilan yangilanadigan kesh kerak)
  • Side effect lar (email yuborish) — ikkinchi chaqiruvda bajarilmaydi
  • Argumentlar o'zgaruvchan (list, dict)

[!WARNING] lru_cache argumentlar hashable bo'lishini talab qiladi: @lru_cache li funksiyani ro'yxat bilan chaqirsangiz — TypeError: unhashable type: 'list'. Tuple ga aylantiring. Va metodlarga (self bilan) qo'llashda ehtiyot bo'ling: kesh ob'ektga havolani ushlab turadi va u xotiradan tozalanmaydi.


wraps — dekoratorlar uchun

23-darsda ko'rgan:

from functools import wraps

def log(f):
    @wraps(f)                  # f ning nomi va docstring ini saqlash
    def orovchi(*args, **kwargs):
        print(f"{f.__name__} chaqirildi")
        return f(*args, **kwargs)
    return orovchi

Boshqa foydali funksiyalar

total_ordering

29-darsda: __eq__ va __lt__ dan barcha taqqoslash operatorlarini yaratadi.

cached_property

from functools import cached_property

class Hisobot:
    def __init__(self, sotuvlar):
        self.sotuvlar = sotuvlar

    @cached_property
    def jami(self):
        print("Hisoblanmoqda...")
        return sum(self.sotuvlar)

h = Hisobot([100, 200, 300])
print(h.jami)     # Hisoblanmoqda... 600
print(h.jami)     # 600 — qayta hisoblanmaydi

Bir marta hisoblanadigan og'ir property (28-dars) uchun.

singledispatch — tur bo'yicha har xil funksiya

from functools import singledispatch

@singledispatch
def formatla(qiymat):
    return str(qiymat)

@formatla.register
def _(qiymat: float):
    return f"{qiymat:.2f}"

@formatla.register
def _(qiymat: list):
    return ", ".join(formatla(x) for x in qiymat)

print(formatla(3.14159))       # 3.14
print(formatla([1, 2.5, "a"])) # 1, 2.50, a

if isinstance(...) zanjirlari o'rniga.


Tez-tez uchraydigan xatolar

Vaqtga bog'liq funksiyani keshlash — eskirgan ma'lumot abadiy qaytadi.

lru_cache ga list uzatish — TypeError: unhashable type.

reduce ni sum o'rniga:

reduce(lambda a, b: a + b, sonlar)   # ishlaydi, lekin
sum(sonlar)                          # ✅ aniqroq va tezroq

Xulosa

functools — funksiyalarni sozlash, keshlash va birlashtirish vositalari.

Eslab qoling:

  • partial(f, ...) — argumentlari oldindan to'ldirilgan yangi funksiya
  • reduce(f, iterable, boshlang'ich) — bitta qiymatga yig'ish; ko'pincha sum/math.prod/sikl aniqroq
  • @lru_cache(maxsize=...) / @cache — pure funksiya natijalarini keshlash; cache_info(), cache_clear()
  • Kesh — faqat hashable argumentlar va vaqtga bog'liq bo'lmagan natijalar uchun
  • @wraps, @total_ordering, @cached_property, @singledispatch

Keyingi dars

48-dars: Type hints — list[int], Optional, Union, dict[str, int], TypeVar