Mundarija (20)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Bytecode: sukut qachon quriladi
- 2.2. Qachon "bir marta" va qachon "har safar"
- 2.3. Tuzoqning boshqa shakllari
- 2.4. Sukut qiymat — yashirin havola
- 2.5. Funksiyani yangi sukutlar bilan klonlash
- 2.6. Nega Python shunday ishlaydi
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Bytecode va hayot davri
- Misol 2 — Tuzoqning boshqa shakllari
- Misol 3 — O'zgaruvchan sukutlarni avtomatik topish
- Misol 4 — Amaliy: sukutdagi holat, testlar va to'g'ri muqobillar
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
11.6-dars: O'zgaruvchan standart argument tuzog'i
11-QISM — OBYEKT MODELI VA XOTIRA · 6-dars
1. Kirish va motivatsiya
7.3-darsda mashhur tuzoqni batafsil ko'rdik: def qosh(x, royxat=[]) — sukut qiymat bir marta yaratiladi, __defaults__ da saqlanadi va barcha chaqiruvlar uni bo'lishadi. Sentinel va None yechimlarini, B006 ogohlantirishini va sukut qiymatni ataylab ishlatish holatlarini o'rgandik.
Endi bu tuzoqqa obyekt modeli ko'zi bilan qaraymiz. Chunki u faqat funksiya parametrlarida emas. Mana dataclass tekshiruvidan "o'tib ketgan" kod:
@dataclass
class Buyurtma:
raqam: int
savat: Savat = Savat() # ✅ dataclass xato bermadi
b1, b2 = Buyurtma(1), Buyurtma(2)
b1.savat.qosh("non")
b2.savat.mahsulotlar # ['non'] — boshqa mijozning savati!dataclass list, dict va set sukutlarini taqiqlaydi — lekin o'z klassingiz nusxasini tanimaydi. Tuzoqning mohiyati o'zgarmagan: bir marta yaratilgan o'zgaruvchan obyekt ko'p egaga bo'lishilgan.
Bu darsda tuzoqni uch darajada ko'ramiz: bytecode'da sukut qiymat qachon yaratiladi; qaysi boshqa joylarda (klass, dataclass, NamedTuple, partial, __init__) xuddi shu mexanizm ishlaydi; va bunday holatlarni avtomatik topuvchi vosita qanday yoziladi.
Bu darsda:
- Bytecode: sukut qiymatlar
defbajarilganda qanday quriladi - Ichma-ich
defva sikldagidef— har safar yangi sukut - Tuzoqning boshqa shakllari:
__init__,dataclass,NamedTuple,partial, klass atributlari - Sukut qiymat — yashirin havola: xotira va iplar
- Funksiyani yangi sukutlar bilan klonlash (testlar uchun)
- Avtomatik aniqlovchi:
inspectvaastbilan
2. Nazariya — chuqur tushuntirish
2.1. Bytecode: sukut qachon quriladi
def tashqi():
def ichki(a=(1, 2), b=[1, 2]):
...
return ichkidis.dis(tashqi):
LOAD_CONST ((1, 2)) ← o'zgarmas kortej — kompilyatsiyada tayyor konstanta
LOAD_SMALL_INT 1
LOAD_SMALL_INT 2
BUILD_LIST 2 ← ro'yxat HAR SAFAR def bajarilganda quriladi
BUILD_TUPLE 2 ← sukutlar korteji
LOAD_CONST <code object ichki>
MAKE_FUNCTION
SET_FUNCTION_ATTRIBUTE 1 (defaults) ← kortej funksiyaning __defaults__ iga yoziladi
STORE_FAST ichkiUch fakt:
- Sukut ifodalari
defqatori bajarilganda hisoblanadi — funksiya chaqirilganda emas - Natija funksiya obyektining
__defaults__(pozitsion) va__kwdefaults__(faqat nomli) atributlarida havola sifatida saqlanadi - Funksiya chaqirilganda sukut ifodasi qayta bajarilmaydi —
__defaults__dagi obyekt parametrga bog'lanadi
2.2. Qachon "bir marta" va qachon "har safar"
def qayerda |
def necha marta bajariladi |
Sukut obyekt |
|---|---|---|
| Modul darajasida | Import paytida bir marta | Bitta — dastur yashagan paytgacha |
| Klass tanasida (metod) | Klass yaratilganda bir marta | Bitta — barcha nusxalar uchun |
| Boshqa funksiya ichida | Tashqi funksiya har chaqirilganda | Har yangi ichki funksiya — yangi sukut |
| Sikl ichida | Har iteratsiyada | Har funksiya — o'z sukuti |
lambda x=[]: ... |
lambda ifodasi bajarilganda |
Xuddi def kabi |
Sikl ichidagi def ning sukuti alohida bo'lsa ham, funksiya tanasidagi tashqi o'zgaruvchi (i) kechikkan bog'lanishga uchraydi (10.12-dars) — ikki mexanizmni chalkashtirmang.
2.3. Tuzoqning boshqa shakllari
Mexanizm bir xil: bir marta yaratilgan o'zgaruvchan obyekt ko'p joyga bog'lanadi.
| Shakl | Kod | Kim bo'lishadi |
|---|---|---|
__init__ sukuti |
def __init__(self, teglar=[]): self.teglar = teglar |
Sukut bilan yaratilgan barcha nusxalar |
| Klass atributi | class K: teglar = [] |
Barcha nusxalar (11.3-dars) |
dataclass maydoni |
savat: Savat = Savat() |
Barcha nusxalar — list/dict/set dan tashqari turlar tekshirilmaydi |
NamedTuple maydoni |
teglar: list = [] |
Barcha nusxalar — hech qanday tekshiruv yo'q |
functools.partial |
partial(f, []) |
Shu partial ning barcha chaqiruvlari |
| Modul konstantasi | STANDART_TEGLAR = ["yangi"] qaytarilib, o'zgartirilsa |
Butun dastur |
dict.fromkeys(k, []) |
Barcha kalitlar (11.3-dars) |
dataclass qoidasi (3.11+): sukut qiymat xeshlanmaydigan bo'lsa (__hash__ None) — ValueError: mutable default ... use default_factory. Oddiy klass nusxasining xeshi bor — tekshiruvdan o'tadi.
Yechimlar:
dataclass—field(default_factory=Savat)NamedTuple— o'zgarmas sukut ((),frozenset()), yokiNone+ fabrika metodi__init__—teglar=None→self.teglar = list(teglar) if teglar is not None else []partial— o'zgaruvchan argumentni bog'lamang yoki har chaqiruvda yangi yarating
2.4. Sukut qiymat — yashirin havola
__defaults__ dagi obyekt funksiya yashagan paytgacha tirik (11.4-dars):
- Xotira:
def f(x, kesh={})— kesh cheksiz o'sadi va hech qachon bo'shamaydi - Iplar: bir nechta ip bir sukut ro'yxatga yozsa — poyga holati va aralash ma'lumot (14-qism)
- Testlar: birinchi test sukutni o'zgartirsa, keyingi testlar "iflos" holatda boshlanadi — natija testlar tartibiga bog'liq
2.5. Funksiyani yangi sukutlar bilan klonlash
Funksiya — kod obyekti + globals + sukutlar + closure. Yangi sukutlar bilan nusxa:
import types
toza = types.FunctionType(f.__code__, f.__globals__, f.__name__,
f.__defaults__ and tuple(copy.deepcopy(f.__defaults__)),
f.__closure__)
toza.__kwdefaults__ = copy.deepcopy(f.__kwdefaults__)Bu tuzatish va testlar uchun vosita (masalan, uchinchi tomon kodidagi "iflos" sukutli funksiyani testlar orasida tiklash) — ishlab chiqarish kodida sukutni to'g'ri yozish kerak.
2.6. Nega Python shunday ishlaydi
- Soddalik va tezlik: sukut bir marta hisoblanadi, chaqiruvda qo'shimcha ish yo'q
- Erta bog'lash foydali:
lambda i=i: i— qiymatnidefpaytida "qotirish" (10.12-dars) - Izchillik:
def— bajariladigan ifoda; sukut ifodasi ham shu paytda bajariladi
Kechikkan (har chaqiruvda hisoblanadigan) sukutlar uchun PEP 671 taklifi bor (def f(x=>[])), lekin u tilga qabul qilinmagan.
3. Tez ma'lumotnoma
Mexanizm
def bajarilganda → sukut ifodasi hisoblanadi → __defaults__ / __kwdefaults__
chaqiruvda → o'sha obyekt parametrga bog'lanadi
o'zgarmas konstanta → co_consts dan; ro'yxat/lug'at → BUILD_LIST/BUILD_MAP def paytidaQachon bir marta
modul def, metod (klass tanasi) → bitta sukut
ichki def (har tashqi chaqiruv), sikl def → har safar yangiBoshqa shakllar
__init__(self, x=[]) dataclass: x: K = K() NamedTuple: x: list = []
partial(f, []) class K: x = [] dict.fromkeys(k, [])Yechimlar
def f(x=None): x = [] if x is None else x
field(default_factory=Savat)
self.teglar = list(teglar) if teglar is not None else []Tekshirish
f.__defaults__, f.__kwdefaults__
dataclasses.fields(K)[i].default
NamedTuple._field_defaults4. Batafsil misollar
Misol 1 — Bytecode va hayot davri
"""Sukut qiymat def paytida quriladi: dis, __defaults__ identifikatsiyasi, ichki va sikldagi def, xotira o'sishi."""
import dis
def opkodlar(funksiya):
return [i.opname for i in dis.get_instructions(funksiya)
if i.opname in ("LOAD_CONST", "BUILD_LIST", "BUILD_MAP", "BUILD_TUPLE", "MAKE_FUNCTION",
"SET_FUNCTION_ATTRIBUTE", "CALL")]
def tashqi():
def ichki(a=(1, 2), b=[1, 2], c={"k": 1}):
return a, b, c
return ichki
print("=== 1. def paytidagi amallar ===")
print(f" {opkodlar(tashqi)}")
print(" o'zgarmas kortej — LOAD_CONST, ro'yxat va lug'at — har def da BUILD_LIST / BUILD_MAP")
print("\n=== 2. Modul darajasidagi funksiya: bitta sukut ===")
def qosh(qiymat, royxat=[]):
royxat.append(qiymat)
return royxat
natijalar = [qosh(i) for i in range(3)]
print(f" natijalar: {natijalar}")
print(f" hammasi bitta obyekt: {all(n is qosh.__defaults__[0] for n in natijalar)}")
print("\n=== 3. Ichki def: har tashqi chaqiruv — yangi sukut ===")
f1, f2 = tashqi(), tashqi()
f1()[1].append(99)
print(f" f1 sukuti o'zgartirildi → f1: {f1()[1]}, f2: {f2()[1]}, bitta obyekt: {f1.__defaults__[1] is f2.__defaults__[1]}")
print(f" o'zgarmas kortej esa bitta konstanta: {f1.__defaults__[0] is f2.__defaults__[0]}")
print("\n=== 4. Sikldagi def: alohida sukut, lekin kechikkan i ===")
funksiyalar = []
for i in range(3):
def yozuv(x=[]):
x.append(i)
return x
funksiyalar.append(yozuv)
print(f" sukutlar alohida: {funksiyalar[0].__defaults__[0] is not funksiyalar[1].__defaults__[0]}")
print(f" natijalar: {[f() for f in funksiyalar]} ⚠️ i — global, chaqiruv paytidagi qiymat")
funksiyalar2 = []
for i in range(3):
def yozuv2(x=None, i=i):
x = [] if x is None else x
x.append(i)
return x
funksiyalar2.append(yozuv2)
print(f" ✅ i=i va x=None: {[f() for f in funksiyalar2]}")
print("\n=== 5. Sukut — yashirin havola: kesh cheksiz o'sadi ===")
def narx_ol(mahsulot, _kesh={}):
if mahsulot not in _kesh:
_kesh[mahsulot] = len(mahsulot) * 1000
return _kesh[mahsulot]
for i in range(10_000):
narx_ol(f"mahsulot-{i}")
print(f" 10 000 noyob so'rovdan keyin sukut lug'atida: {len(narx_ol.__defaults__[0])} yozuv — hech qachon bo'shamaydi")Natijaning muhim qismi:
=== 1. def paytidagi amallar ===
['LOAD_CONST', 'BUILD_LIST', 'LOAD_CONST', 'BUILD_MAP', 'BUILD_TUPLE', 'LOAD_CONST', 'MAKE_FUNCTION', 'SET_FUNCTION_ATTRIBUTE']
o'zgarmas kortej — LOAD_CONST, ro'yxat va lug'at — har def da BUILD_LIST / BUILD_MAP
=== 2. Modul darajasidagi funksiya: bitta sukut ===
natijalar: [[0, 1, 2], [0, 1, 2], [0, 1, 2]]
hammasi bitta obyekt: True
=== 3. Ichki def: har tashqi chaqiruv — yangi sukut ===
f1 sukuti o'zgartirildi → f1: [1, 2, 99], f2: [1, 2], bitta obyekt: False
o'zgarmas kortej esa bitta konstanta: True
=== 4. Sikldagi def: alohida sukut, lekin kechikkan i ===
sukutlar alohida: True
natijalar: [[2], [2], [2]] ⚠️ i — global, chaqiruv paytidagi qiymat
✅ i=i va x=None: [[0], [1], [2]]
=== 5. Sukut — yashirin havola: kesh cheksiz o'sadi ===
10 000 noyob so'rovdan keyin sukut lug'atida: 10000 yozuv — hech qachon bo'shamaydiNima ko'rsatdi: 2.1, 2.2, 2.4-bo'limlar.
Misol 2 — Tuzoqning boshqa shakllari
"""__init__, dataclass (tekshiruvdan o'tadigan maxsus klass), NamedTuple, partial, modul konstantasi."""
import functools
from dataclasses import dataclass, field
from typing import NamedTuple
class Savat:
def __init__(self):
self.mahsulotlar = []
def qosh(self, m):
self.mahsulotlar.append(m)
print("=== 1. __init__ sukuti ===")
class Mijoz:
def __init__(self, ism, teglar=[]):
self.ism = ism
self.teglar = teglar
a, b = Mijoz("Aziz"), Mijoz("Dilnoza")
a.teglar.append("VIP")
print(f" Dilnoza teglari: {b.teglar}, bitta ro'yxat: {a.teglar is b.teglar}")
class MijozToza:
def __init__(self, ism, teglar=None):
self.ism = ism
self.teglar = list(teglar) if teglar is not None else []
a, b = MijozToza("Aziz"), MijozToza("Dilnoza")
a.teglar.append("VIP")
print(f" ✅ None + list(): Dilnoza teglari: {b.teglar}")
print("\n=== 2. dataclass: list taqiqlanadi, maxsus klass — yo'q ===")
try:
@dataclass
class Buyurtma1:
raqam: int
savat: list = []
except ValueError as e:
print(f" list sukut: ValueError: {e}")
@dataclass
class Buyurtma2:
raqam: int
savat: Savat = Savat()
b1, b2 = Buyurtma2(1), Buyurtma2(2)
b1.savat.qosh("non")
print(f" ⚠️ Savat() sukut: b2 savati {b2.savat.mahsulotlar}, bitta obyekt: {b1.savat is b2.savat}")
print(f" Savat xeshlanadimi: {Savat.__hash__ is not None} — tekshiruv shu bo'yicha")
@dataclass
class Buyurtma3:
raqam: int
savat: Savat = field(default_factory=Savat)
b1, b2 = Buyurtma3(1), Buyurtma3(2)
b1.savat.qosh("non")
print(f" ✅ default_factory: b2 savati {b2.savat.mahsulotlar}")
print("\n=== 3. NamedTuple — tekshiruv umuman yo'q ===")
class Nuqta(NamedTuple):
x: int
teglar: list = []
p1, p2 = Nuqta(1), Nuqta(2)
p1.teglar.append("boshlang'ich")
print(f" p2.teglar: {p2.teglar}, _field_defaults: {Nuqta._field_defaults}")
print("\n=== 4. functools.partial ===")
def yozuvga_qosh(qiymat, jurnal):
jurnal.append(qiymat)
return jurnal
tizim_jurnali = functools.partial(yozuvga_qosh, jurnal=[])
tizim_jurnali("ishga tushdi")
tizim_jurnali("ulanish")
print(f" partial ning barcha chaqiruvlari bitta ro'yxatga: {tizim_jurnali.keywords['jurnal']}")
print("\n=== 5. Modul konstantasini qaytarish ===")
STANDART_RUXSATLAR = ["o'qish"]
def ruxsatlar(rol):
if rol == "mehmon":
return STANDART_RUXSATLAR
return STANDART_RUXSATLAR + ["yozish"]
ruxsatlar("mehmon").append("o'chirish")
print(f" mehmon ruxsatlari endi: {ruxsatlar('mehmon')}, konstanta: {STANDART_RUXSATLAR}")
STANDART_RUXSATLAR_TOZA = ("o'qish",)
print(f" ✅ tuple konstanta: {STANDART_RUXSATLAR_TOZA} — append imkonsiz")Natijaning muhim qismi:
=== 1. __init__ sukuti ===
Dilnoza teglari: ['VIP'], bitta ro'yxat: True
✅ None + list(): Dilnoza teglari: []
=== 2. dataclass: list taqiqlanadi, maxsus klass — yo'q ===
list sukut: ValueError: mutable default <class 'list'> for field savat is not allowed: use default_factory
⚠️ Savat() sukut: b2 savati ['non'], bitta obyekt: True
Savat xeshlanadimi: True — tekshiruv shu bo'yicha
✅ default_factory: b2 savati []
=== 3. NamedTuple — tekshiruv umuman yo'q ===
p2.teglar: ["boshlang'ich"], _field_defaults: {'teglar': ["boshlang'ich"]}
=== 4. functools.partial ===
partial ning barcha chaqiruvlari bitta ro'yxatga: ['ishga tushdi', 'ulanish']
=== 5. Modul konstantasini qaytarish ===
mehmon ruxsatlari endi: ["o'qish", "o'chirish"], konstanta: ["o'qish", "o'chirish"]
✅ tuple konstanta: ("o'qish",) — append imkonsizNima ko'rsatdi: 2.3-bo'lim. dataclass faqat xeshlanmaydigan sukutlarni ushlaydi — oddiy klass nusxasi object dan xesh meros olgani uchun tekshiruvdan o'tdi.
Misol 3 — O'zgaruvchan sukutlarni avtomatik topish
"""Ish vaqtida (inspect, dataclasses, NamedTuple) va statik (ast) tekshiruv: har ikki usul nimani topadi va nimani o'tkazib yuboradi."""
import ast
import dataclasses
import functools
import inspect
import textwrap
import types
from typing import NamedTuple
OZGARMAS = (int, float, complex, str, bytes, bool, type(None), tuple, frozenset, type, types.FunctionType, range)
def ozgaruvchanmi(qiymat):
if isinstance(qiymat, OZGARMAS):
return isinstance(qiymat, tuple) and any(ozgaruvchanmi(x) for x in qiymat)
return True
def ish_vaqtida_tekshir(nomlar_maydoni, modul_nomi):
topilgan = []
for nom, obj in nomlar_maydoni.items():
if not isinstance(obj, functools.partial) and getattr(obj, "__module__", None) != modul_nomi:
continue # import qilingan narsalar — boshqa modulniki
funksiyalar = []
if inspect.isfunction(obj):
funksiyalar.append((nom, obj))
elif inspect.isclass(obj):
if dataclasses.is_dataclass(obj):
for f in dataclasses.fields(obj):
if f.default is not dataclasses.MISSING and ozgaruvchanmi(f.default):
topilgan.append(f"{nom}.{f.name}: dataclass sukuti {type(f.default).__name__}")
if issubclass(obj, tuple) and hasattr(obj, "_field_defaults"):
for maydon, qiymat in obj._field_defaults.items():
if ozgaruvchanmi(qiymat):
topilgan.append(f"{nom}.{maydon}: NamedTuple sukuti {type(qiymat).__name__}")
for mnom, m in vars(obj).items():
if inspect.isfunction(m):
if not (dataclasses.is_dataclass(obj) and mnom == "__init__"): # maydonlar yuqorida
funksiyalar.append((f"{nom}.{mnom}", m))
elif not mnom.startswith("_") and not callable(m) and not hasattr(type(m), "__get__") \
and ozgaruvchanmi(m) and not dataclasses.is_dataclass(obj):
topilgan.append(f"{nom}.{mnom}: klass atributi {type(m).__name__}")
elif isinstance(obj, functools.partial):
for q in list(obj.args) + list(obj.keywords.values()):
if ozgaruvchanmi(q):
topilgan.append(f"{nom}: partial argumenti {type(q).__name__}")
for fnom, f in funksiyalar:
imzo = inspect.signature(f)
for p in imzo.parameters.values():
if p.default is not p.empty and ozgaruvchanmi(p.default):
topilgan.append(f"{fnom}({p.name}=...): sukut {type(p.default).__name__}")
return topilgan
MANBA = textwrap.dedent('''
from dataclasses import dataclass, field
from typing import NamedTuple
import functools
class Savat:
def __init__(self):
self.m = []
def qosh(x, royxat=[]):
royxat.append(x)
def toza(x, royxat=None, *, teglar=()):
return x
def nomli(*, sozlama={}):
return sozlama
class Mijoz:
standart_teglar = ["yangi"]
def __init__(self, ism, teglar=set()):
self.teglar = teglar
@dataclass
class Buyurtma:
raqam: int
savat: Savat = Savat()
teglar: list = field(default_factory=list)
class Nuqta(NamedTuple):
x: int
izohlar: list = []
jurnal = functools.partial(print, [])
''')
print("=== 1. Ish vaqtida (kod bajarilgach) ===")
nomlar = {"__name__": "manba"}
exec(MANBA, nomlar)
for qator in ish_vaqtida_tekshir({k: v for k, v in nomlar.items() if not k.startswith("__")}, "manba"):
print(f" {qator}")
class StatikTekshiruvchi(ast.NodeVisitor):
OZGARUVCHAN_TUGUNLAR = (ast.List, ast.Dict, ast.Set, ast.ListComp, ast.DictComp, ast.SetComp)
OZGARUVCHAN_CHAQIRUVLAR = {"list", "dict", "set", "defaultdict", "deque", "bytearray"}
def __init__(self):
self.topilgan = []
def _xavfli(self, tugun):
if isinstance(tugun, self.OZGARUVCHAN_TUGUNLAR):
return True
return isinstance(tugun, ast.Call) and isinstance(tugun.func, ast.Name) \
and tugun.func.id in self.OZGARUVCHAN_CHAQIRUVLAR
def visit_FunctionDef(self, tugun):
for sukut in tugun.args.defaults + [d for d in tugun.args.kw_defaults if d is not None]:
if self._xavfli(sukut):
self.topilgan.append(f"{tugun.lineno}-qator {tugun.name}(): sukut {ast.unparse(sukut)}")
self.generic_visit(tugun)
def visit_ClassDef(self, tugun):
for element in tugun.body:
qiymat = getattr(element, "value", None)
if isinstance(element, (ast.Assign, ast.AnnAssign)) and qiymat is not None and self._xavfli(qiymat):
self.topilgan.append(f"{element.lineno}-qator {tugun.name}: klass darajasida {ast.unparse(qiymat)}")
self.generic_visit(tugun)
print("\n=== 2. Statik (ast, kod bajarilmasdan) ===")
t = StatikTekshiruvchi()
t.visit(ast.parse(MANBA))
for qator in t.topilgan:
print(f" {qator}")
print("\n=== 3. Solishtirish ===")
print(" ish vaqtida: Savat() dataclass sukutini va partial argumentini topdi (obyekt turini biladi)")
print(" statik: Savat() ni ko'rmadi (qaysi klass o'zgaruvchanligini bilmaydi), lekin kodni bajarmaydi")Natijaning muhim qismi:
=== 1. Ish vaqtida (kod bajarilgach) ===
qosh(royxat=...): sukut list
nomli(sozlama=...): sukut dict
Mijoz.standart_teglar: klass atributi list
Mijoz.__init__(teglar=...): sukut set
Buyurtma.savat: dataclass sukuti Savat
Nuqta.izohlar: NamedTuple sukuti list
jurnal: partial argumenti list
=== 2. Statik (ast, kod bajarilmasdan) ===
10-qator qosh(): sukut []
16-qator nomli(): sukut {}
20-qator Mijoz: klass darajasida ['yangi']
21-qator __init__(): sukut set()
32-qator Nuqta: klass darajasida []
=== 3. Solishtirish ===
ish vaqtida: Savat() dataclass sukutini va partial argumentini topdi (obyekt turini biladi)
statik: Savat() ni ko'rmadi (qaysi klass o'zgaruvchanligini bilmaydi), lekin kodni bajarmaydiNima ko'rsatdi: 2.3-bo'lim. Ikki usul bir-birini to'ldiradi: statik tahlil (ruff ning B006, B008, RUF012 qoidalari ham shunday) importsiz ishlaydi, ish vaqtidagi tekshiruv esa haqiqiy obyektlarni ko'radi.
Misol 4 — Amaliy: sukutdagi holat, testlar va to'g'ri muqobillar
Uchinchi tomon kutubxonasidagi funksiya o'zgaruvchan sukutda kesh saqlaydi va testlar bir-biriga ta'sir qiladi. Uni testlar orasida qanday tiklash va o'z kodimizda qaysi muqobil to'g'riligini ko'ramiz.
"""Sukutdagi kesh testlarni bog'laydi; types.FunctionType bilan toza klon; muqobillar: lru_cache, klass, closure."""
import copy
import types
from functools import lru_cache
def kurs_ol(valyuta, _kesh={}, _sorovlar=[]):
"""'Uchinchi tomon' funksiyasi: tarmoq so'rovlarini sukutdagi keshda saqlaydi."""
if valyuta not in _kesh:
_sorovlar.append(valyuta)
_kesh[valyuta] = {"USD": 12_600, "EUR": 13_700}.get(valyuta, 0)
return _kesh[valyuta]
def test_bitta_sorov(f):
f("USD")
f("USD")
sorovlar = f.__defaults__[1]
return "✅" if sorovlar == ["USD"] else f"❌ so'rovlar: {sorovlar}"
def test_ikki_valyuta(f):
f("USD")
f("EUR")
sorovlar = f.__defaults__[1]
return "✅" if sorovlar == ["USD", "EUR"] else f"❌ so'rovlar: {sorovlar}"
def toza_nusxa(f):
yangi = types.FunctionType(f.__code__, f.__globals__, f.__name__,
copy.deepcopy(f.__defaults__), f.__closure__)
yangi.__kwdefaults__ = copy.deepcopy(f.__kwdefaults__)
yangi.__doc__ = f.__doc__
return yangi
TOZA_HOLAT = toza_nusxa(kurs_ol) # hali hech kim chaqirmagan paytdagi nusxa
print("=== 1. ⚠️ Testlar tartibga bog'liq ===")
print(f" test_ikki_valyuta: {test_ikki_valyuta(kurs_ol)}")
print(f" test_bitta_sorov: {test_bitta_sorov(kurs_ol)} (oldingi test sukutdagi ro'yxatni to'ldirib qo'ygan)")
print("\n=== 2. ✅ Har test uchun toza klon ===")
print(f" test_ikki_valyuta: {test_ikki_valyuta(toza_nusxa(TOZA_HOLAT))}")
print(f" test_bitta_sorov: {test_bitta_sorov(toza_nusxa(TOZA_HOLAT))}")
print(f" asl funksiya hali ham iflos: {kurs_ol.__defaults__[1]}")
print("\n=== 3. O'z kodimizda — ochiq muqobillar ===")
tarmoq = []
@lru_cache(maxsize=128)
def kurs_lru(valyuta):
tarmoq.append(valyuta)
return {"USD": 12_600, "EUR": 13_700}.get(valyuta, 0)
class KursXizmati:
def __init__(self, manba):
self._manba = manba
self._kesh = {}
self.sorovlar = []
def ol(self, valyuta):
if valyuta not in self._kesh:
self.sorovlar.append(valyuta)
self._kesh[valyuta] = self._manba(valyuta)
return self._kesh[valyuta]
def kurs_yasa(manba):
kesh = {}
def ol(valyuta):
if valyuta not in kesh:
kesh[valyuta] = manba(valyuta)
return kesh[valyuta]
ol.tozala = kesh.clear
return ol
manba = {"USD": 12_600, "EUR": 13_700}.get
for v in ["USD", "USD", "EUR"]:
kurs_lru(v)
print(f" lru_cache: so'rovlar {tarmoq}, {kurs_lru.cache_info()}; testda cache_clear()")
xizmat = KursXizmati(manba)
for v in ["USD", "USD", "EUR"]:
xizmat.ol(v)
print(f" klass: so'rovlar {xizmat.sorovlar}; testda yangi KursXizmati(soxta_manba)")
ol = kurs_yasa(manba)
print(f" closure: {ol('USD')}, {ol('USD')}; testda yangi kurs_yasa() yoki ol.tozala()")
print("\n=== 4. Sukut qiymatni ataylab ishlatish — erta bog'lash ===")
tugmalar = [lambda raqam=raqam: f"tugma {raqam}" for raqam in range(3)]
print(f" {[t() for t in tugmalar]} (o'zgarmas int — xavfsiz)")Natijaning muhim qismi:
=== 1. ⚠️ Testlar tartibga bog'liq ===
test_ikki_valyuta: ✅
test_bitta_sorov: ❌ so'rovlar: ['USD', 'EUR'] (oldingi test sukutdagi ro'yxatni to'ldirib qo'ygan)
=== 2. ✅ Har test uchun toza klon ===
test_ikki_valyuta: ✅
test_bitta_sorov: ✅
asl funksiya hali ham iflos: ['USD', 'EUR']
=== 3. O'z kodimizda — ochiq muqobillar ===
lru_cache: so'rovlar ['USD', 'EUR'], CacheInfo(hits=1, misses=2, maxsize=128, currsize=2); testda cache_clear()
klass: so'rovlar ['USD', 'EUR']; testda yangi KursXizmati(soxta_manba)
closure: 12600, 12600; testda yangi kurs_yasa() yoki ol.tozala()
=== 4. Sukut qiymatni ataylab ishlatish — erta bog'lash ===
['tugma 0', 'tugma 1', 'tugma 2'] (o'zgarmas int — xavfsiz)Nima ko'rsatdi: 2.4, 2.5-bo'limlar — sukutdagi holat testlarni tartibga bog'laydi; klon bilan izolyatsiya mumkin, lekin o'z kodimizda holatni lru_cache, klass yoki closure'da ochiq saqlash to'g'ri yo'l.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Sukut qiymat har chaqiruvda hisoblanadi" | def bajarilganda bir marta — __defaults__ da |
"Tuzoq faqat def f(x=[]) da" |
__init__, dataclass, NamedTuple, partial, klass atributlari — hammasida |
"dataclass o'zgaruvchan sukutlarni to'liq taqiqlaydi" |
Faqat xeshlanmaydiganlarni (list, dict, set) |
"NamedTuple o'zgarmas — sukuti ham xavfsiz" |
Kortej o'zgarmas, ichidagi ro'yxat esa yo'q |
| "Ichki funksiyadagi sukut ham bitta" | Har tashqi chaqiruv yangi funksiya va yangi sukut |
"Sikldagi def lar bir xil sukutni bo'lishadi" |
Har biri o'ziniki; lekin tanadagi tashqi o'zgaruvchi kechikkan |
| "Sukutdagi kesh — zararsiz optimallash" | Cheksiz o'sadi, testlarni va iplarni bog'laydi |
"O'zgarmas sukut ((), 0, None) ham xavfli" |
Yo'q — xavf faqat o'zgaruvchan obyektda |
6. Keng tarqalgan xatolar va yechimlari
1. __init__ da o'zgaruvchan sukutni to'g'ridan-to'g'ri saqlash
def __init__(self, teglar=[]):
self.teglar = teglar # ❌
def __init__(self, teglar=None):
self.teglar = list(teglar) if teglar is not None else [] # ✅ nusxa ham2. dataclass da maxsus klass nusxasi sukut
@dataclass
class Buyurtma:
savat: Savat = Savat() # ❌ tekshiruvdan o'tadi, lekin bo'lishiladi
savat: Savat = field(default_factory=Savat) # ✅3. NamedTuple da ro'yxat sukut
class Nuqta(NamedTuple):
teglar: list = [] # ❌
teglar: tuple = () # ✅4. partial ga o'zgaruvchan argument
jurnalga = partial(qosh, jurnal=[]) # ❌ hamma chaqiruv bitta ro'yxatga
jurnal = [] # ✅ aniq nom — egalik ko'rinib turadi
jurnalga = partial(qosh, jurnal=jurnal)5. Modul konstantasini to'g'ridan-to'g'ri qaytarish
STANDART = ["o'qish"]
def ruxsatlar():
return STANDART # ❌ chaqiruvchi o'zgartiradi
STANDART = ("o'qish",) # ✅ yoki return list(STANDART)6. Sukutda vaqt yoki tasodif
def yozuv(matn, vaqt=datetime.now()): # ❌ import paytidagi vaqt (7.3-dars)
def yozuv(matn, vaqt=None):
vaqt = vaqt or datetime.now() # ✅7. Sukutdagi kesh
def narx(m, _kesh={}): ... # ❌ cheksiz o'sish, testlar bog'lanadi
@lru_cache(maxsize=1024) # ✅ chegarali va tozalanadigan
def narx(m): ...8. None sukut va or bilan bo'sh qiymatni yo'qotish
def f(royxat=None):
royxat = royxat or [] # ❌ chaqiruvchining bo'sh ro'yxati [] yangisi bilan almashadi
royxat = [] if royxat is None else royxat # ✅ bo'sh ro'yxat — haqiqiy argument7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 7.3-dars (o'tilgan): tuzoqning asosiy mexanizmi va yechimlari
- 8.17-dars (o'tilgan):
dataclass,field(default_factory=...) - 10.12-dars (o'tilgan): erta bog'lash —
lambda i=ining foydali tomoni - 11.3–11.4-darslar (o'tilgan): taxalluslar va yashirin havolalar
- 12-qism:
MAKE_FUNCTION,SET_FUNCTION_ATTRIBUTEva kod obyektlari - 13-qism:
pydanticmodellari — sukutlarni har nusxa uchun nusxalaydi - 14-qism: iplar orasida umumiy o'zgaruvchan sukut — poyga holati
- 17-qism: testlar izolyatsiyasi;
B006,B008qoidalari - 18-qism:
ruffbilan avtomatik tekshiruv
8. Eng yaxshi amaliyotlar
Sukut qiymatlar o'zgarmas bo'lsin —
None,(),frozenset(), sonlar, satrlar.dataclassda o'zgaruvchan maydonlar uchun har doimdefault_factory—listbo'lmasa ham.__init__da qabul qilingan to'plamni nusxalang.Nonesukutniis Nonebilan tekshiring,orbilan emas.Holatli kesh — ochiq joyda:
lru_cache, klass atributi yoki closure; sukutda emas.partialva konstantalarga o'zgaruvchan obyekt bog'lamang.ruffningB006/B008/RUF012qoidalarini yoqing va ish vaqtidagi tekshiruvni testlarga qo'shing.Erta bog'lash kerak bo'lsa — faqat o'zgarmas qiymatlar bilan (
lambda i=i).
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
from dataclasses import dataclass
from typing import NamedTuple
import functools
1. def f(x=[]):
x.append(1)
return len(x)
print(f(), f(), f([]))
2. print(f.__defaults__)
3. def g(n, x=None):
x = [] if x is None else x
x.append(n)
return x
print(g(1), g(2))
4. class K:
def __init__(self, t=[]):
self.t = t
a = K()
b = K()
a.t.append("x")
print(b.t, a.t is b.t)
5. class S:
pass
@dataclass
class D:
s: S = S()
print(D().s is D().s)
6. try:
@dataclass
class E:
l: list = []
except ValueError:
print("ValueError")
7. class N(NamedTuple):
t: list = []
N().t.append(1)
print(N().t)
8. fs = []
for i in range(3):
def h(x=[]):
return x
fs.append(h)
print(fs[0]() is fs[1]())
9. def yasa():
def ichki(x=[]):
x.append(1)
return x
return ichki
print(yasa()(), yasa()())
10. p = functools.partial(lambda x: x.append(1) or x, [])
print(p(), p())
11. def v(t=(1, 2)):
return t
print(v() is v())
12. def w(*, k=[]):
k.append(0)
return k
w()
print(w.__kwdefaults__)Javoblar
1 2 1— uchinchi chaqiruv o'z ro'yxatini berdi([1, 1],)— sukut ikki marta o'zgargan[1] [2]['x'] TrueTrue—S()bir marta yaratilgan va xeshlanadi,dataclasstekshirmadiValueError[1]—NamedTuplesukuti bo'lishiladiFalse— hardefo'z sukutiga ega[1] [1]— haryasa()yangi ichki funksiya[1, 1] [1, 1]— ikkala chaqiruv bitta ro'yxatni o'zgartirib, uni qaytardi;printikkalasini bajarilgandan keyin chiqaradiTrue— bitta kortej konstanta{'k': [0]}
Vazifa 2: Xatolarni tuzating
1. @dataclass
class Sessiya:
foydalanuvchi: str
savat: Savat = Savat()
sozlama: dict = None
2. class Hisobot:
def __init__(self, qatorlar=[], filtrlar={}):
self.qatorlar = qatorlar
self.filtrlar = filtrlar
3. class Marshrut(NamedTuple):
nomi: str
bekatlar: list = []
4. def jurnal(xabar, darajalar=["INFO"], vaqt=time.time()):
return f"[{vaqt}] {darajalar[0]}: {xabar}"
5. def birlashtir(asosiy, qoshimcha=None):
qoshimcha = qoshimcha or {}
asosiy.update(qoshimcha)
return asosiyJavoblar
1. @dataclass
class Sessiya:
foydalanuvchi: str
savat: Savat = field(default_factory=Savat)
sozlama: dict = field(default_factory=dict) # None va keyin tekshirish o'rniga
2. class Hisobot:
def __init__(self, qatorlar=None, filtrlar=None):
self.qatorlar = list(qatorlar) if qatorlar is not None else []
self.filtrlar = dict(filtrlar) if filtrlar is not None else {}
3. class Marshrut(NamedTuple):
nomi: str
bekatlar: tuple = ()
4. def jurnal(xabar, darajalar=("INFO",), vaqt=None):
vaqt = time.time() if vaqt is None else vaqt
return f"[{vaqt}] {darajalar[0]}: {xabar}"
5. def birlashtir(asosiy, qoshimcha=None):
natija = dict(asosiy) # chaqiruvchi lug'atini o'zgartirmaymiz
if qoshimcha is not None:
natija.update(qoshimcha)
return natijaVazifa 3: muzlatilgan_sukutlar dekoratori
Funksiya dekoratori yozing:
- Har chaqiruvda
__defaults__va__kwdefaults__dagi o'zgaruvchan obyektlarning nusxasini parametrga bersin (asl sukutlar hech qachon o'zgarmasin) - Nusxalash usuli: sayoz (
copy.copy) yoki chuqur (copy.deepcopy) — parametr inspect.signaturevafunctools.wrapssaqlansin- Tezlikni oddiy funksiya bilan solishtiring va qachon bu dekorator o'rinli ekanini yozing
Vazifa 4: ruff qoidasi analogi
Misol 3 dagi statik tekshiruvchini kengaytiring:
field(default=[]),dict.fromkeys(..., []),partial(f, [])holatlarini topsin- Sukutda funksiya chaqiruvini (
datetime.now(),uuid4()) aniqlasin (B008analogi) - Loyiha papkasidagi barcha
.pyfayllarni aylanib, hisobotnifayl:qatorformatida chiqarsin # noqa: SUKUTizohi bilan belgilangan qatorlarni o'tkazib yuborsin
Vazifa 5: Test izolyatori
Pytest fixture'iga o'xshash kontekst menejer yozing: with sukutlarni_tikla(modul):
- Blokka kirishda moduldagi barcha funksiya va metodlarning sukutlarini
deepcopyqilib saqlasin - Blokdan chiqqanda ularni tiklasin (
__defaults__va__kwdefaults__ga yozish) dataclassmaydonlari vaNamedTuple._field_defaultsni ham qamrab olsin- Misol 4 dagi testlarni shu menejer bilan izolyatsiya qiling
Vazifa 6: Iplar va sukut
def yig(qiymat, royxat=[])funksiyasini 8 ip × 10 000 chaqiruv bilan ishga tushiring- Natijaviy ro'yxat uzunligini va aralashgan tartibni tahlil qiling
- Xuddi shuni har ipga alohida ro'yxat berib takrorlang
- Nega bu xato bitta ipli testlarda ko'rinmasligini tushuntiring
Vazifa 7: O'ylash
JavaScript, Ruby, Kotlin va C++ da sukut qiymat ifodasi har chaqiruvda (yoki har chaqiruv joyida) hisoblanadi. Python esa uni def paytida bir marta hisoblaydi. Bu tanlovning afzallik va kamchiliklari nimada? PEP 671 dagi "kechikkan sukutlar" (def f(x=>[])) taklifi qanday muammoni hal qilardi va qanday savollar tug'diradi?
Javob
Qisqa javob: Python'da def — oddiy bajariladigan ifoda, sukut ifodasi ham shu paytda bir marta hisoblanadi. Bu sodda model (funksiya obyektida tayyor qiymatlar korteji) va arzon chaqiruv beradi, qiymatni "qotirish" (erta bog'lash) imkonini ham beradi. Narxi — o'zgaruvchan obyektlar bilan klassik tuzoq va None + tekshiruvning takrorlanuvchi shabloni. Boshqa tillar sukutni chaqiruv paytida hisoblaydi — tuzoq yo'q, lekin sukut ifodasi har safar qayta bajariladi va erta bog'lash uchun boshqa usul kerak.
1. Boshqa tillar
JavaScript:
function qosh(x, royxat = []) {
royxat.push(x);
return royxat;
}
qosh(1); // [1]
qosh(2); // [2] — har chaqiruvda yangi massivSukut ifodasi chaqiruv paytida hisoblanadi va hatto oldingi parametrlarga murojaat qila oladi: function f(a, b = a * 2).
Ruby: def qosh(x, royxat = []) — xuddi shunday, har chaqiruvda yangi massiv.
Kotlin: fun qosh(x: Int, royxat: MutableList<Int> = mutableListOf()) — sukut chaqiruvda hisoblanadi.
C++: void f(std::vector<int> v = {}) — sukut argument har chaqiruv joyida qo'yiladi va hisoblanadi.
2. Python tanlovining afzalliklari
| Afzallik | Tafsilot |
|---|---|
| Sodda model | Funksiya = kod + __defaults__ korteji; hech qanday "yashirin ifoda" saqlanmaydi |
| Arzon chaqiruv | Sukut qayta hisoblanmaydi |
| Erta bog'lash | lambda i=i: i — sikl o'zgaruvchisini qotirish (10.12-dars) |
| Introspeksiya | inspect.signature haqiqiy qiymatlarni ko'rsatadi, help() ularni chop etadi |
| Izchillik | def — bajariladigan ifoda, dekoratorlar va klass tanasi bilan bir xil vaqt modeli |
3. Kamchiliklari
- O'zgaruvchan sukut tuzog'i — eng ko'p uchraydigan boshlang'ich xatolardan biri
x=None+if x is None: x = []shabloni har joyda takrorlanadi- Sukutda vaqt, tasodif yoki boshqa parametrga bog'liq qiymatni ifodalab bo'lmaydi
- Tuzoq
dataclass,NamedTuple,partialkabi boshqa joylarda ham qaytadan paydo bo'ladi (bu dars)
4. PEP 671: kechikkan sukutlar
Taklif (muallifi Chris Angelico) yangi sintaksis qo'shadi:
def qosh(x, royxat=>[]): # har chaqiruvda yangi ro'yxat
def kesish(s, boshi=0, oxiri=>len(s)): # oldingi parametrga bog'liqHal qiladigan muammolar:
Noneshablonisiz yangi o'zgaruvchan sukut- Sukutni boshqa parametrlardan hisoblash
inspect.signatureda haqiqiy niyat ko'rinadi (Noneemas)
Tug'diradigan savollar:
__defaults__da nima saqlanadi — hisoblanmagan ifoda qanday ifodalanadi,help()vainspectuni qanday ko'rsatadi?- Ikki xil sukut semantikasi (
=va=>) tilni murakkablashtiradi va o'rgatishni qiyinlashtiradi - Chaqiruvchi "sukutni ishlat" deb aniq aytmoqchi bo'lsa (masalan, argumentlarni uzatib yuboruvchi o'rovchilarda) —
Nonebilan bo'lgani kabi qiymat yo'q - Mavjud kod bilan orqaga moslik va vositalar (tur tekshiruvchilar, IDE)
Taklif muhokama qilingan, lekin tilga qabul qilinmagan — hozircha None/sentinel shabloni va dataclass dagi default_factory asosiy yechim.
5. Amaliy xulosa
- Python modelini qabul qiling: sukut —
defpaytidagi qiymat - O'zgaruvchan yoki dinamik sukut uchun
None/sentinel yokidefault_factory - Linter qoidalari (
B006,B008,RUF012) tuzoqni avtomatik ushlaydi - Erta bog'lash kerak bo'lsa — faqat o'zgarmas qiymatlar bilan
6. Xulosa
- JavaScript, Ruby, Kotlin va C++ sukutni chaqiruvda hisoblaydi — tuzoq yo'q, lekin har chaqiruvda qo'shimcha ish
- Python sukutni
defpaytida bir marta hisoblaydi — sodda, arzon, introspeksiyaga qulay va erta bog'lashga imkon beradi - Narxi — o'zgaruvchan sukut tuzog'i va
Noneshabloni - PEP 671 kechikkan sukutlarni taklif qiladi, lekin semantika va vositalar bo'yicha savollar tug'diradi va tilga qabul qilinmagan
- Amalda — o'zgarmas sukutlar,
default_factoryva linter
Nimani mustahkamlaydi: 2.1–2.6-bo'limlar.
Xulosa
Bu darsda o'zgaruvchan standart argument tuzog'ini obyekt modeli nuqtai nazaridan ko'rib chiqdik.
Eng muhim uch fikr:
Sukut qiymat
defbajarilganda quriladi va funksiya obyektida yashaydi. Bytecode'da o'zgarmas kortej tayyor konstanta, ro'yxat va lug'at esa hardefbajarilgandaBUILD_LIST/BUILD_MAPbilan quriladi va__defaults__/__kwdefaults__ga yoziladi. Modul va klass darajasidagi funksiyalar uchun bu bir marta, ichki funksiyalar va sikldagideflar uchun esa har safar — sikldagi tashqi o'zgaruvchining kechikkan bog'lanishi esa alohida masala.Tuzoq faqat funksiya parametrlarida emas.
__init__sukuti, klass atributi,dataclassmaydoni,NamedTuplemaydoni,functools.partialargumenti va qaytarilgan modul konstantasi — hammasi bir marta yaratilgan o'zgaruvchan obyektni ko'p egaga bo'lishadi.dataclassfaqat xeshlanmaydigan sukutlarni ushlaydi: oddiy klass nusxasi tekshiruvdan o'tadi —default_factoryni har doim ishlating.Sukutdagi holat — yashirin, cheksiz yashovchi havola. U xotirani to'playdi, iplarni va testlarni bir-biriga bog'laydi. Testlarda funksiyani
types.FunctionTypebilan yangi sukutlar bilan klonlash mumkin, lekin o'z kodingizda holatnilru_cache, klass yoki closure'da ochiq saqlang varuffqoidalari hamdainspect/astasosidagi tekshiruvlar bilan tuzoqni avtomatik ushlang.
Keyingi darsda interning va kichik obyektlar keshini o'rganamiz: nega a is b ba'zi sonlar va satrlar uchun True, sys.intern qachon foydali, va kompilyator konstantalarni qanday birlashtiradi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!