IlmHamroh
Python kursi/Funksiyalar8/14-dars35 daqiqa
Mundarija (21)

7.8-dars: LEGB — ko'rinish sohasi (scope)

7-QISM — FUNKSIYALAR · 8-dars


1. Kirish va motivatsiya

Python nomni qanday topadi?

python
x = "global"

def f():
    x = "lokal"
    print(x)                # qaysi x?

f()                         # "lokal"
print(x)                    # "global"

Oddiy ko'rinadi. Lekin:

python
x = 10

def f():
    print(x)                # ❌ UnboundLocalError
    x = 20

f()

Nega print(x) xato beradi, garchi x global bo'lsa ham?

Va:

python
def tashqi():
    x = 1
    def ichki():
        x = 2               # tashqi x o'zgardimi?
    ichki()
    return x                # 1 yoki 2?

Va eng chalkash:

python
class A:
    q = [1, 2, 3]
    ikkilangan = [x * 2 for x in q]              # ✅
    juftliklar = [(x, y) for x in q for y in q]  # ❌ NameError

Bu darsda:

  • LEGB — nom qidirish tartibi
  • UnboundLocalError va uning sababi
  • Yopilma (closure) va __closure__
  • Sinf qamrovi — maxsus holat
  • Kompilyatsiya paytida aniqlanish
  • globals(), locals(), vars()

2. Nazariya — chuqur tushuntirish

2.1. LEGB qoidasi

Python nomni to'rt qamrovda ketma-ket qidiradi:

L — Local        (funksiya ichi)
E — Enclosing    (tashqi funksiya)
G — Global       (modul)
B — Built-in     (ichki nomlar)
python
x = "global"                        # G

def tashqi():
    x = "enclosing"                 # E
    def ichki():
        x = "lokal"                 # L
        print(x)                    # "lokal"
    ichki()

tashqi()

Har darajani olib tashlab ko'ring:

python
x = "global"

def tashqi():
    x = "enclosing"
    def ichki():
        print(x)                    # "enclosing" — L yo'q
    ichki()
python
x = "global"

def tashqi():
    def ichki():
        print(x)                    # "global" — L va E yo'q
    ichki()
python
def f():
    print(len)                      # <built-in function len> — B

Topilmasa — NameError:

python
def f():
    print(aniqlanmagan)             # ❌ NameError

Built-in qamrovni ko'rish:

python
import builtins
print(len(dir(builtins)))           # ~150 nom
print([n for n in dir(builtins) if not n.startswith("_")][:10])

Ichki nomni yopish:

python
list = [1, 2]                       # ⚠️ G darajada
list("abc")                         # ❌ TypeError — G da topildi, B ga yetmadi

2.2. Kompilyatsiya paytida aniqlanadi

Eng muhim tushuncha: Python o'zgaruvchi lokalmi yoki yo'qmi ni kompilyatsiya paytida hal qiladi — kod bajarilishidan oldin.

Qoida: funksiya tanasida nomga o'zlashtirish bo'lsa, u butun funksiya davomida lokal.

python
x = 10

def f():
    print(x)                        # ❌ UnboundLocalError
    x = 20                          # ← BU qator x ni lokal qildi

x = 20 qatori funksiyaning oxirida bo'lsa ham, x butun funksiyada lokal. print(x) bajarilganda u hali qiymat olmagan.

Xato nomi aniq: UnboundLocalError — "lokal, lekin bog'lanmagan".

python
def f():
    print(x)
# UnboundLocalError: cannot access local variable 'x' where it is not
# associated with a value

Bytecode farqi:

python
def global_oqish():
    return x                        # LOAD_GLOBAL

def lokal_oqish():
    x = 1
    return x                        # LOAD_FAST

def aralash():
    print(x)                        # LOAD_FAST  ← lokal deb hisoblanadi!
    x = 1

co_varnames — lokal nomlar ro'yxati:

python
def f():
    print(x)
    x = 1

f.__code__.co_varnames              # ('x',)  ← x lokal
f.__code__.co_names                 # ()      ← global nom yo'q

O'zlashtirishning barcha shakllari nomni lokal qiladi:

python
def f():
    x = 1                           # oddiy
    x += 1                          # ⚠️ ham o'zlashtirish
    for x in r: ...                 # ⚠️
    with a as x: ...                # ⚠️
    except E as x: ...              # ⚠️
    import x                        # ⚠️
    def x(): ...                    # ⚠️
    class x: ...                    # ⚠️
    x: int = 1                      # ⚠️
    (x := 1)                        # ⚠️ walrus
    del x                           # ⚠️ ham!

del ham nomni lokal qiladi:

python
x = 10

def f():
    del x                           # ❌ UnboundLocalError

Faqat tur ko'rsatkichi — o'zlashtirish EMAS:

python
x = 10

def f():
    x: int                          # ⚠️ o'zlashtirish yo'q
    print(x)                        # ❌ UnboundLocalError (baribir lokal!)

Tur ko'rsatkichi ham nomni lokal qiladi (__annotations__ uchun), lekin qiymat bermaydi.

2.3. Yopilma (closure)

Ichki funksiya tashqi funksiya o'zgaruvchisini eslab qoladi:

python
def kopaytiruvchi(n):
    def ichki(x):
        return x * n                # ⭐ n — enclosing qamrovdan
    return ichki

ikkilash = kopaytiruvchi(2)
ikkilash(5)                         # 10

n tashqi funksiya tugagandan keyin ham yashaydi.

__closure__ — yopilma hujayralari:

python
ikkilash.__closure__                # (<cell at 0x...: int object at 0x...>,)
ikkilash.__closure__[0].cell_contents    # 2
ikkilash.__code__.co_freevars       # ('n',)

Bir necha o'zgaruvchi:

python
def yaratuvchi(a, b, c):
    def ichki():
        return a + b                # faqat a va b ishlatiladi
    return ichki

f = yaratuvchi(1, 2, 3)
f.__code__.co_freevars              # ('a', 'b')  — c yo'q

Python faqat ishlatilgan o'zgaruvchilarni saqlaydi.

Yopilma o'zgaruvchisini o'zgartirib bo'lmaydi:

python
def tashqi():
    x = 1
    def ichki():
        x = 2                       # ⚠️ YANGI lokal x
    ichki()
    return x                        # 1 — o'zgarmadi

Buning uchun nonlocal kerak (9-dars).

Kech bog'lanish (late binding):

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

[f() for f in funksiyalar]          # [2, 2, 2]  ⚠️

lambda i ni hisoblamaydi, unga havola saqlaydi. Sikl tugaganda i == 2.

Yechim:

python
funksiyalar = [lambda i=i: i for i in range(3)]      # ✅ sukut argument

Bu — 6.15-darsda ko'rilgan.

Yopilma o'zgaruvchan obyekt bilan:

python
def sanagich():
    hisob = [0]                     # ⚠️ ro'yxat — o'zgartirish mumkin
    def ichki():
        hisob[0] += 1               # ✅ obyektni o'zgartiradi
        return hisob[0]
    return ichki

Bu — nonlocal dan oldingi hiyla. Endi:

python
def sanagich():
    hisob = 0
    def ichki():
        nonlocal hisob              # ✅ aniqroq
        hisob += 1
        return hisob
    return ichki

2.4. Sinf qamrovi — maxsus holat

Sinf tanasi — alohida qamrov, lekin u LEGB zanjirida qatnashmaydi:

python
x = "global"

class A:
    x = "sinf"
    def metod(self):
        return x                    # "global"  ⚠️ sinf x emas!

A().metod()                         # "global"

Sinf atributiga murojaat uchun self.x yoki A.x kerak.

Generatorlar bilan tuzoq:

python
class A:
    q = [1, 2, 3]
    ikkilangan = [x * 2 for x in q]              # ✅
    juftliklar = [(x, y) for x in q for y in q]  # ❌ NameError: q

Sabab:

  • Generator — yashirin funksiya (6.15-dars)
  • Birinchi for ning iterable'i tashqi qamrovda hisoblanadi → q ko'rinadi
  • Qolgan hamma narsa generator ichida → sinf qamrovi ko'rinmaydi

Yechim:

python
Q = [1, 2, 3]                       # modul darajasida

class A:
    q = Q
    juftliklar = [(x, y) for x in Q for y in Q]      # ✅

Yoki metod ichida:

python
class A:
    q = [1, 2, 3]

    @classmethod
    def juftliklar(cls):
        return [(x, y) for x in cls.q for y in cls.q]    # ✅

Nega shunday: sinf tanasi funksiya emas — u bir marta bajariladi va natija type() ga uzatiladi. Uning nom fazosi funksiyalar uchun "ko'rinmas" qilingan — aks holda metodlar ichida sinf atributlariga bevosita murojaat mumkin bo'lardi, va bu self ni ma'nosiz qilardi.

2.5. globals(), locals(), vars()

python
globals()                           # modul nom fazosi (dict)
locals()                            # joriy qamrov (dict)
vars()                              # locals() bilan bir xil
vars(obj)                           # obj.__dict__

globals() — haqiqiy lug'at, o'zgartirish ishlaydi:

python
globals()["yangi"] = 42
print(yangi)                        # 42

locals() — funksiya ichida NUSXA:

python
def f():
    x = 1
    locals()["x"] = 99
    return x                        # 1  ⚠️ o'zgarmadi

def g():
    locals()["yangi"] = 1
    return yangi                    # ❌ NameError

Sabab: funksiya lokal o'zgaruvchilari massivda (co_varnames indekslari bo'yicha), lug'atda emas. locals() har chaqiruvda yangi lug'at quradi.

Modul darajasida locals() is globals():

python
locals() is globals()               # True (modul darajasida)

Amaliy qo'llanish:

python
# Debug
def f(a, b):
    print(f"argumentlar: {locals()}")

# Formatlash (⚠️ ehtiyotkorlik bilan)
ism, yosh = "Aziz", 30
"{ism} ({yosh})".format(**locals())

# Dinamik nom (⚠️ deyarli har doim noto'g'ri)
globals()[f"o_{i}"] = qiymat        # ❌ lug'at ishlating

Python 3.13 dan: locals() semantikasi PEP 667 bilan aniqlashtirildi — optimallashtirilgan qamrovlarda u endi mustaqil nusxa.

2.6. Qamrov va bytecode

CPython uch xil yuklash instruksiyasi ishlatadi:

Instruksiya Qamrov Tezlik
LOAD_FAST Lokal Eng tez (massiv indeksi)
LOAD_DEREF Yopilma Tez (cell obyekti)
LOAD_GLOBAL Global/built-in Sekinroq (lug'at qidiruvi)
python
import dis

def f():
    x = 1
    return x + len([1]) + GLOBAL_NOM

dis.dis(f)
#   LOAD_FAST     x          ← lokal
#   LOAD_GLOBAL   len        ← built-in
#   LOAD_GLOBAL   GLOBAL_NOM ← global

Tezlik farqi:

python
# Sekinroq — har iteratsiyada global qidiruv
def sekin(r):
    natija = []
    for x in r:
        natija.append(len(str(x)))      # len va str — global
    return natija

# Tezroq — lokal nomga ko'chirilgan
def tez(r, _len=len, _str=str):
    natija = []
    qosh = natija.append
    for x in r:
        qosh(_len(_str(x)))
    return natija

Bu — mikro-optimallashtirish. Zamonaviy CPython (3.11+) LOAD_GLOBAL ni keshlaydi, farq kichraydi. O'qilishni qurbon qilmang.

2.7. Qamrov nozikliklari

1. Modul darajasidagi for — lokal emas:

python
for i in range(3):
    pass
print(i)                            # 2  ✅ ko'rinadi

Funksiya ichida ham:

python
def f():
    for i in range(3):
        pass
    return i                        # 2  ✅

Python da for alohida qamrov yaratmaydi (C, Java dan farqli).

2. if/while/with ham qamrov yaratmaydi:

python
if True:
    x = 1
print(x)                            # 1  ✅

3. except ... as x — blokdan keyin O'CHIRILADI:

python
try:
    raise ValueError
except ValueError as x:
    pass
print(x)                            # ❌ NameError

Bu — ataylab: x istisno obyektiga havola saqlaydi, u esa traceback orqali freym'ga havola qiladi → aylanma havola va xotira sizishi.

Saqlash uchun:

python
xato = None
try:
    ...
except ValueError as x:
    xato = x                        # ✅ boshqa nomga

4. Generator ifodasi — o'z qamrovi:

python
x = 10
[x for x in range(3)]
print(x)                            # 10  ✅ (Python 3)

5. Lambda — funksiya qamrovi:

python
x = 10
f = lambda: x
x = 20
f()                                 # 20  ⚠️ kech bog'lanish

6. import — lokal bo'lishi mumkin:

python
def f():
    import math                     # ⚠️ math lokal
    return math.pi

math.pi                             # ❌ NameError (agar tashqarida import qilinmagan bo'lsa)

3. Tez ma'lumotnoma

LEGB tartibi

L — Local        funksiya ichi
E — Enclosing    tashqi funksiya (yopilma)
G — Global       modul
B — Built-in     len, print, ...

Topilmasa → NameError

Kompilyatsiya paytida aniqlanadi

python
x = 10
def f():
    print(x)        ❌ UnboundLocalError
    x = 20          ← BU qator x ni LOKAL qildi

Tanada o'zlashtirish bo'lsa → butun funksiyada LOKAL

O'zlashtirish shakllari:
  x = 1,  x += 1,  for x in,  with as x,  except as x,
  import x,  def x,  class x,  x: int,  (x := 1),  del x

Yopilma

python
def tashqi(n):
    def ichki(): return n       ⭐ n eslab qolinadi
    return ichki

f.__closure__[0].cell_contents
f.__code__.co_freevars

⚠️ x = 2 ichki funksiyada → YANGI lokal (nonlocal kerak)
⚠️ [lambda: i for i in range(3)] → hammasi 2 (kech bog'lanish)

Sinf qamrovi

python
class A:
    x = 1
    def m(self): return x       ⚠️ GLOBAL x, sinf x emas
    y = [i for i in x]          ✅ birinchi for
    z = [(i,j) for i in x for j in x]   ❌ NameError

Sinf qamrovi LEGB zanjirida QATNASHMAYDI

globals/locals

python
globals()           haqiqiy dict — o'zgartirish ishlaydi
locals()            funksiyada NUSXA — o'zgartirish ishlamaydi
vars()              locals() bilan bir xil
vars(obj)           obj.__dict__

Qamrov yaratmaydi

for, while, if, with, try     ✅ qamrov YO'Q — o'zgaruvchi ko'rinadi
except ... as x               ⚠️ blokdan keyin O'CHIRILADI
generator/lambda/def/class    ✅ o'z qamrovi

4. Batafsil misollar

Misol 1 — LEGB namoyishi

python
"""To'rt qamrov va qidirish tartibi."""

import builtins

print("=== 1. To'rt daraja ===")

x = "GLOBAL"


def tashqi():
    x = "ENCLOSING"

    def ichki():
        x = "LOKAL"
        return x

    return ichki(), x


def tashqi_lokalsiz():
    x = "ENCLOSING"

    def ichki():
        return x                    # L yo'q → E

    return ichki()


def tashqi_bosh():
    def ichki():
        return x                    # L va E yo'q → G

    return ichki()


def built_in():
    return len                      # L, E, G yo'q → B


print(f"  x = 'GLOBAL' (modul darajasida)\n")
print(f"  {'Holat':<32} {'Natija'}")
print("  " + "─" * 56)
print(f"  {'L bor':<32} {tashqi()[0]!r}")
print(f"  {'L yo`q, E bor':<32} {tashqi_lokalsiz()!r}")
print(f"  {'L va E yo`q, G bor':<32} {tashqi_bosh()!r}")
print(f"  {'Faqat B':<32} {built_in()}")

print(f"\n  Topilmasa:")
try:
    def f():
        return aniqlanmagan_nom
    f()
except NameError as e:
    print(f"    ❌ NameError: {e}")


print("\n=== 2. Built-in qamrov ===")

ichki_nomlar = [n for n in dir(builtins) if not n.startswith("_")]
print(f"  Jami ichki nom: {len(ichki_nomlar)}\n")

TOIFALAR = {
    "Funksiyalar": [n for n in ichki_nomlar
                    if callable(getattr(builtins, n)) and n[0].islower()],
    "Turlar": [n for n in ichki_nomlar
               if isinstance(getattr(builtins, n), type)
               and not n.endswith("Error") and not n.endswith("Warning")],
    "Istisnolar": [n for n in ichki_nomlar if n.endswith(("Error", "Exception"))],
    "Konstantalar": [n for n in ichki_nomlar
                     if not callable(getattr(builtins, n))],
}

for toifa, nomlar in TOIFALAR.items():
    print(f"  {toifa} ({len(nomlar)}):")
    print(f"    {', '.join(sorted(nomlar)[:12])}...")

print(f"\n  ⚠️ Ichki nomni yopish:")
print(f"""
    list = [1, 2]           # G darajada
    list("abc")             # ❌ TypeError — G da topildi, B ga yetmadi
""")

_asl_list = list
list = [1, 2]
try:
    list("abc")
except TypeError as e:
    print(f"    ❌ TypeError: {e}")
list = _asl_list
print(f"    Tiklandi: list('abc') = {list('abc')}")


print("\n=== 3. ⭐ UnboundLocalError ===")


def yaxshi():
    return x                        # G da topiladi


def yomon():
    print(x)                        # ⚠️ x lokal deb hisoblanadi
    x = 20


print(f"  x = {x!r}\n")
print(f"  def f(): return x")
print(f"    → {yaxshi()!r}   ✅\n")

print(f"  def f():")
print(f"      print(x)")
print(f"      x = 20")
try:
    yomon()
except UnboundLocalError as e:
    print(f"    → ❌ UnboundLocalError: {e}")

print(f"\n  Sabab — bytecode:")
import dis
for f, nom in [(yaxshi, "return x"), (yomon, "print(x); x = 20")]:
    instruksiyalar = [i.opname for i in dis.get_instructions(f)
                      if i.opname.startswith("LOAD")]
    print(f"    {nom:<24} {instruksiyalar}")

print(f"\n  co_varnames (lokal nomlar):")
for f, nom in [(yaxshi, "yaxshi"), (yomon, "yomon")]:
    print(f"    {nom:<10} varnames={f.__code__.co_varnames}, "
          f"names={f.__code__.co_names}")


print("\n=== 4. O'zlashtirishning barcha shakllari ===")

y = "GLOBAL_Y"

SHAKLLAR = [
    ("y = 1",               "def f():\n    print(y)\n    y = 1"),
    ("y += 1",              "def f():\n    print(y)\n    y += 1"),
    ("for y in ...",        "def f():\n    print(y)\n    for y in [1]: pass"),
    ("with ... as y",       "def f():\n    print(y)\n    with open(__file__) as y: pass"),
    ("except ... as y",     "def f():\n    print(y)\n    try: pass\n    except Exception as y: pass"),
    ("import y",            "def f():\n    print(y)\n    import y"),
    ("def y()",             "def f():\n    print(y)\n    def y(): pass"),
    ("class y",             "def f():\n    print(y)\n    class y: pass"),
    ("y: int",              "def f():\n    print(y)\n    y: int"),
    ("(y := 1)",            "def f():\n    print(y)\n    (y := 1)"),
    ("del y",               "def f():\n    print(y)\n    del y"),
]

print(f"  Har birida `print(y)` avval keladi:\n")
print(f"  {'Shakl':<20} {'Natija'}")
print("  " + "─" * 56)

for nom, kod in SHAKLLAR:
    muhit = {"y": "GLOBAL_Y", "open": open, "__file__": __file__,
             "Exception": Exception, "print": lambda *a: None}
    try:
        exec(kod, muhit)
        muhit["f"]()
        natija = "✅ global topildi"
    except UnboundLocalError:
        natija = "❌ UnboundLocalError (lokal)"
    except Exception as e:
        natija = f"❌ {type(e).__name__}"
    print(f"  {nom:<20} {natija}")

print(f"\n  ⭐ HAMMASI nomni lokal qiladi — hatto `del` va `y: int` ham")


print("\n=== 5. Qamrov yaratmaydigan bloklar ===")

TEKSHIRUVLAR = [
    ("for", """
for i in range(3):
    pass
natija = i
"""),
    ("while", """
i = 0
while i < 3:
    i += 1
natija = i
"""),
    ("if", """
if True:
    j = "if ichida"
natija = j
"""),
    ("with", """
import io
with io.StringIO("x") as f:
    k = "with ichida"
natija = k
"""),
    ("try", """
try:
    m = "try ichida"
except Exception:
    pass
natija = m
"""),
    ("except as", """
try:
    raise ValueError("x")
except ValueError as e:
    pass
natija = e
"""),
    ("generator", """
n = "asl"
[n for n in range(3)]
natija = n
"""),
]

print(f"  {'Blok':<14} {'Blokdan keyin o`zgaruvchi'}")
print("  " + "─" * 52)
for nom, kod in TEKSHIRUVLAR:
    muhit = {}
    try:
        exec(kod, muhit)
        natija = f"✅ {muhit['natija']!r}"
    except NameError as e:
        natija = f"❌ NameError: {e}"
    print(f"  {nom:<14} {natija}")

print("""
  ⭐ for, while, if, with, try — qamrov YARATMAYDI
  ⚠️ except ... as e — blokdan keyin O'CHIRILADI (aylanma havola oldini olish)
  ⭐ generator — O'Z qamrovi (Python 3)
""")


print("\n=== 6. globals() va locals() ===")

MODUL_NOM = "modul qiymati"


def qamrovlarni_korsat(a, b=2):
    lokal = "lokal qiymat"

    def ichki():
        return "ichki"

    print(f"    locals():  {sorted(locals())}")
    print(f"    globals() da 'MODUL_NOM': {'MODUL_NOM' in globals()}")
    print(f"    locals() is globals(): {locals() is globals()}")
    return locals()


print(f"  Funksiya ichida:")
natija = qamrovlarni_korsat(1)

print(f"\n  Modul darajasida:")
print(f"    locals() is globals(): {locals() is globals()}")

print(f"\n  ⚠️ locals() o'zgartirish:")


def locals_ozgartirish():
    x = 1
    locals()["x"] = 99
    return x


print(f"    def f(): x = 1; locals()['x'] = 99; return x")
print(f"    → {locals_ozgartirish()}   ⚠️ o'zgarmadi (nusxa)")

print(f"\n  ✅ globals() o'zgartirish:")
globals()["yangi_global"] = 42
print(f"    globals()['yangi_global'] = 42")
print(f"    yangi_global = {yangi_global}   ✅ ishladi")
del globals()["yangi_global"]

print("""
  ⭐ Sabab: funksiya lokallari MASSIVDA (co_varnames indekslari),
     lug'atda emas. locals() har chaqiruvda yangi lug'at quradi.

     Modul darajasida esa haqiqatan lug'at ishlatiladi.
""")

Natijaning muhim qismi:

text
=== 1. To'rt daraja ===
  Holat                            Natija
  ────────────────────────────────────────────────────────
  L bor                            'LOKAL'
  L yo`q, E bor                    'ENCLOSING'
  L va E yo`q, G bor               'GLOBAL'
  Faqat B                          <built-in function len>

=== 3. ⭐ UnboundLocalError ===
  def f():
      print(x)
      x = 20
    → ❌ UnboundLocalError: cannot access local variable 'x' where it is
       not associated with a value

  Sabab — bytecode:
    return x                 ['LOAD_GLOBAL']
    print(x); x = 20         ['LOAD_GLOBAL', 'LOAD_FAST_CHECK', 'LOAD_SMALL_INT', 'LOAD_CONST']

=== 5. Qamrov yaratmaydigan bloklar ===
  Blok           Blokdan keyin o`zgaruvchi
  ────────────────────────────────────────────────────
  for            ✅ 2
  if             ✅ 'if ichida'
  except as      ❌ NameError: name 'e' is not defined
  generator      ✅ 'asl'

Nima ko'rsatdi: 2.1, 2.2, 2.5, 2.7-bo'limlar.

Misol 2 — Yopilma

python
"""Closure — tashqi o'zgaruvchini eslab qolish."""

print("=== 1. Asosiy yopilma ===")


def kopaytiruvchi(n):
    def ichki(x):
        return x * n                # n — enclosing qamrovdan
    return ichki


ikkilash = kopaytiruvchi(2)
uchlash = kopaytiruvchi(3)
onlash = kopaytiruvchi(10)

print(f"  ikkilash = kopaytiruvchi(2)")
print(f"  uchlash  = kopaytiruvchi(3)\n")
print(f"    {'x':>4} {'ikkilash':>10} {'uchlash':>10} {'onlash':>10}")
print("    " + "─" * 38)
for x in [1, 5, 10]:
    print(f"    {x:>4} {ikkilash(x):>10} {uchlash(x):>10} {onlash(x):>10}")

print(f"\n  ⭐ kopaytiruvchi() tugagan, lekin n saqlanib qoldi")


print("\n=== 2. __closure__ ichkarisi ===")

print(f"  {'Funksiya':<12} {'co_freevars':<16} {'cell_contents'}")
print("  " + "─" * 48)
for nom, f in [("ikkilash", ikkilash), ("uchlash", uchlash),
               ("onlash", onlash)]:
    freevars = f.__code__.co_freevars
    qiymatlar = tuple(c.cell_contents for c in f.__closure__)
    print(f"  {nom:<12} {str(freevars):<16} {qiymatlar}")

print(f"\n  Yopilmasiz funksiya:")


def oddiy(x):
    return x * 2


print(f"    oddiy.__closure__ = {oddiy.__closure__}")
print(f"    oddiy.co_freevars = {oddiy.__code__.co_freevars}")


print("\n=== 3. Faqat ishlatilgan o'zgaruvchilar ===")


def yaratuvchi(a, b, c, d):
    def faqat_a():
        return a

    def a_va_b():
        return a + b

    def hech_qaysi():
        return 42

    return faqat_a, a_va_b, hech_qaysi


f1, f2, f3 = yaratuvchi(1, 2, 3, 4)

print(f"  def yaratuvchi(a, b, c, d):  — 4 ta o'zgaruvchi\n")
print(f"  {'Ichki funksiya':<16} {'co_freevars':<20} {'Saqlangan'}")
print("  " + "─" * 52)
for nom, f in [("faqat_a", f1), ("a_va_b", f2), ("hech_qaysi", f3)]:
    freevars = f.__code__.co_freevars
    qiymatlar = (tuple(c.cell_contents for c in f.__closure__)
                 if f.__closure__ else ())
    print(f"  {nom:<16} {str(freevars):<20} {qiymatlar}")

print(f"\n  ⭐ Python faqat ISHLATILGAN o'zgaruvchilarni saqlaydi")


print("\n=== 4. ⚠️ O'zgartirib bo'lmaydi ===")


def urinish():
    x = 1

    def ichki():
        x = 2                       # ⚠️ YANGI lokal x
        return x

    ichki_natija = ichki()
    return x, ichki_natija


tashqi_x, ichki_x = urinish()
print(f"  def tashqi():")
print(f"      x = 1")
print(f"      def ichki():")
print(f"          x = 2")
print(f"      ichki()")
print(f"      return x\n")
print(f"    Tashqi x: {tashqi_x}   ⚠️ o'zgarmadi")
print(f"    Ichki x:  {ichki_x}")

print(f"\n  ❌ Faqat o'qish urinishi ham xato beradi:")


def uqish_urinishi():
    x = 1

    def ichki():
        print(x)                    # ⚠️ x lokal deb hisoblanadi
        x = 2

    return ichki()


try:
    uqish_urinishi()
except UnboundLocalError as e:
    print(f"    ❌ UnboundLocalError: {e}")

print(f"\n  ✅ Yechimlar (9-darsda batafsil):")
print(f"""
    1. nonlocal x           — enclosing o'zgaruvchini o'zgartirish
    2. x = [1]              — o'zgaruvchan obyekt (eski hiyla)
       x[0] = 2
    3. Sinf                 — holat saqlash uchun
""")


print("\n=== 5. ⚠️ Kech bog'lanish ===")

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

print(f"  for i in range(3):")
print(f"      funksiyalar.append(lambda: i)\n")
print(f"    [f() for f in funksiyalar] = {[f() for f in funksiyalar_yomon]}")
print(f"    ⚠️ Hammasi 2 — lambda `i` ga HAVOLA saqlaydi\n")

print(f"  ✅ Yechimlar:\n")

f1 = [lambda i=i: i for i in range(3)]
print(f"    1. Sukut argument:")
print(f"       [lambda i=i: i for i in range(3)]  →  {[f() for f in f1]}")

from functools import partial
f2 = [partial(lambda x: x, i) for i in range(3)]
print(f"\n    2. functools.partial:")
print(f"       [partial(lambda x: x, i) ...]      →  {[f() for f in f2]}")


def yaratuvchi_f(i):
    return lambda: i


f3 = [yaratuvchi_f(i) for i in range(3)]
print(f"\n    3. Yaratuvchi funksiya:")
print(f"       [yaratuvchi(i) for i in range(3)]  →  {[f() for f in f3]}")

print(f"\n  Yopilma tarkibi:")
print(f"    Yomon:  {[f.__code__.co_freevars for f in funksiyalar_yomon]}")
print(f"    Yaxshi: {[f.__code__.co_freevars for f in f1]}  (sukut argument — freevar emas)")


print("\n=== 6. Yopilma bilan holat ===")


def sanagich_royxat():
    """Eski hiyla — o'zgaruvchan obyekt."""
    hisob = [0]

    def ichki():
        hisob[0] += 1
        return hisob[0]

    ichki.qiymat = lambda: hisob[0]
    ichki.tozala = lambda: hisob.__setitem__(0, 0)
    return ichki


def sanagich_nonlocal():
    """✅ Zamonaviy — nonlocal (9-dars)."""
    hisob = 0

    def ichki():
        nonlocal hisob
        hisob += 1
        return hisob

    return ichki


class SanagichSinf:
    """✅ Sinf — eng aniq."""

    def __init__(self):
        self.hisob = 0

    def __call__(self):
        self.hisob += 1
        return self.hisob


USULLAR = [
    ("Yopilma + ro'yxat",   sanagich_royxat()),
    ("Yopilma + nonlocal",  sanagich_nonlocal()),
    ("Sinf (__call__)",     SanagichSinf()),
]

print(f"  {'Usul':<22} {'1':>4} {'2':>4} {'3':>4}  Holat")
print("  " + "─" * 54)
for nom, f in USULLAR:
    natijalar = [f() for _ in range(3)]
    if "ro'yxat" in nom:
        holat = "f.__closure__[0].cell_contents[0]"
    elif "nonlocal" in nom:
        holat = "f.__closure__[0].cell_contents"
    else:
        holat = "f.hisob"
    print(f"  {nom:<22} {natijalar[0]:>4} {natijalar[1]:>4} "
          f"{natijalar[2]:>4}  {holat}")


print("\n=== 7. Amaliy: konfiguratsiyalangan funksiyalar ===")


def validator_yarat(min_qiymat=None, max_qiymat=None, turi=None):
    """Konfiguratsiyalangan validator qaytaradi."""
    # turi bitta sinf yoki sinflar korteji bo'lishi mumkin: int yoki (int, float)
    turlar = turi if isinstance(turi, tuple) else (turi,) if turi else ()
    tur_nomi = " | ".join(t.__name__ for t in turlar)

    def tekshir(qiymat):
        if turi is not None and not isinstance(qiymat, turi):
            return False, f"{tur_nomi} kutildi, {type(qiymat).__name__} keldi"
        if min_qiymat is not None and qiymat < min_qiymat:
            return False, f"{qiymat} < {min_qiymat}"
        if max_qiymat is not None and qiymat > max_qiymat:
            return False, f"{qiymat} > {max_qiymat}"
        return True, "ok"

    qismlar = []
    if turi:
        qismlar.append(tur_nomi)
    if min_qiymat is not None:
        qismlar.append(f">={min_qiymat}")
    if max_qiymat is not None:
        qismlar.append(f"<={max_qiymat}")
    tekshir.__name__ = f"validator({', '.join(qismlar)})"
    return tekshir


VALIDATORLAR = {
    "yosh": validator_yarat(0, 150, int),
    "ball": validator_yarat(0, 100, (int, float)),
    "maosh": validator_yarat(min_qiymat=0, turi=int),
}

MALUMOT = [
    ("yosh", 30), ("yosh", -5), ("yosh", 200), ("yosh", "30"),
    ("ball", 85.5), ("ball", 150),
    ("maosh", 5_000_000), ("maosh", -100),
]

print(f"  {'Maydon':<10} {'Qiymat':<14} {'Natija':<8} Sabab")
print("  " + "─" * 56)
for maydon, qiymat in MALUMOT:
    ok, sabab = VALIDATORLAR[maydon](qiymat)
    belgi = "✅" if ok else "❌"
    print(f"  {maydon:<10} {qiymat!r:<14} {belgi:<8} {sabab}")

print(f"\n  Har validator o'z konfiguratsiyasini eslab qoldi:")
for nom, v in VALIDATORLAR.items():
    qiymatlar = {n: c.cell_contents
                 for n, c in zip(v.__code__.co_freevars, v.__closure__)}
    print(f"    {nom:<8} {qiymatlar}")

Natijaning muhim qismi:

text
=== 2. __closure__ ichkarisi ===
  Funksiya     co_freevars      cell_contents
  ────────────────────────────────────────────────
  ikkilash     ('n',)           (2,)
  uchlash      ('n',)           (3,)
  onlash       ('n',)           (10,)

=== 3. Faqat ishlatilgan o'zgaruvchilar ===
  Ichki funksiya   co_freevars          Saqlangan
  ────────────────────────────────────────────────────
  faqat_a          ('a',)               (1,)
  a_va_b           ('a', 'b')           (1, 2)
  hech_qaysi       ()                   ()

=== 5. ⚠️ Kech bog'lanish ===
    [f() for f in funksiyalar] = [2, 2, 2]
    ⚠️ Hammasi 2 — lambda `i` ga HAVOLA saqlaydi

    1. Sukut argument:
       [lambda i=i: i for i in range(3)]  →  [0, 1, 2]

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — Sinf qamrovi

python
"""Sinf tanasi — LEGB dan tashqarida."""

print("=== 1. Sinf atributi metodda ko'rinmaydi ===")

x = "GLOBAL"


class A:
    x = "SINF"

    def metod(self):
        return x                    # ⚠️ GLOBAL

    def self_bilan(self):
        return self.x               # ✅ SINF

    def sinf_bilan(self):
        return A.x                  # ✅ SINF


a = A()
print(f"  x = 'GLOBAL' (modul)")
print(f"  class A: x = 'SINF'\n")
print(f"    a.metod()       → {a.metod()!r}   ⚠️ global topildi")
print(f"    a.self_bilan()  → {a.self_bilan()!r}   ✅")
print(f"    a.sinf_bilan()  → {a.sinf_bilan()!r}   ✅")

print("""
  ⭐ Sinf qamrovi LEGB zanjirida QATNASHMAYDI.
     Metod ichidan sinf atributiga faqat self.x yoki A.x orqali.
""")


print("\n=== 2. ⚠️ Generatorlar bilan tuzoq ===")

print(f"  class A:")
print(f"      q = [1, 2, 3]")
print(f"      ikkilangan = [x*2 for x in q]              # ?")
print(f"      juftliklar = [(x,y) for x in q for y in q] # ?\n")


class B:
    q = [1, 2, 3]
    ikkilangan = [x * 2 for x in q]


print(f"  ✅ ikkilangan = {B.ikkilangan}")

try:
    class C:
        q = [1, 2, 3]
        juftliklar = [(x, y) for x in q for y in q]
except NameError as e:
    print(f"  ❌ juftliklar → NameError: {e}")

print("""
  Sabab:
    • Generator — YASHIRIN FUNKSIYA
    • BIRINCHI `for` ning iterable'i TASHQI qamrovda hisoblanadi
      → sinf qamrovi ko'rinadi ✅
    • Qolgan hamma narsa generator ICHIDA
      → sinf qamrovi KO'RINMAYDI ❌
""")

print(f"  Boshqa buziladigan holatlar:")

MISOLLAR = [
    ("[x for x in q if x in q]",    "ikkinchi q — filtr ichida"),
    ("[q[i] for i in range(3)]",    "q generator tanasida"),
    ("{x: q for x in q}",           "qiymat ifodasida q"),
    ("[i for i in range(len(q))]",  "len(q) — birinchi for ichida ✅"),
]

for kod, izoh in MISOLLAR:
    try:
        exec(f"class T:\n    q = [1,2,3]\n    r = {kod}")
        natija = "✅"
    except NameError:
        natija = "❌ NameError"
    print(f"    {kod:<32} {natija:<14} {izoh}")


print("\n=== 3. Yechimlar ===")

Q = [1, 2, 3]


class D:
    """✅ 1. Modul darajasidagi konstanta."""
    q = Q
    juftliklar = [(x, y) for x in Q for y in Q]


class E:
    """✅ 2. Metod ichida."""
    q = [1, 2, 3]

    @classmethod
    def juftliklar(cls):
        return [(x, y) for x in cls.q for y in cls.q]


class F:
    """✅ 3. Oddiy sikl (generator emas)."""
    q = [1, 2, 3]
    juftliklar = []
    for _x in q:
        for _y in q:
            juftliklar.append((_x, _y))
    del _x, _y


from dataclasses import dataclass, field


@dataclass
class G:
    """✅ 4. dataclass + default_factory."""
    q: list = field(default_factory=lambda: [1, 2, 3])

    @property
    def juftliklar(self):
        return [(x, y) for x in self.q for y in self.q]


print(f"  1. Modul konstantasi:  D.juftliklar[:3] = {D.juftliklar[:3]}")
print(f"  2. Metod ichida:       E.juftliklar()[:3] = {E.juftliklar()[:3]}")
print(f"  3. Oddiy sikl:         F.juftliklar[:3] = {F.juftliklar[:3]}")
print(f"  4. dataclass:          G().juftliklar[:3] = {G().juftliklar[:3]}")


print("\n=== 4. Nega shunday qilingan ===")

print("""
  Agar sinf qamrovi funksiyalardan ko'rinsa:

    class A:
        x = 1
        def metod(self):
            return x            # 1 qaytarardi

  Muammolar:

    1. self ma'nosiz bo'lardi
       — atributga to'g'ridan-to'g'ri murojaat qilinardi

    2. Meros buzilardi
       — qaysi sinfning x i? O'zi yoki ota-onasi?

    3. Obyekt atributi vs sinf atributi
       — a.x = 5 dan keyin metod nimani ko'rardi?

    4. Dinamik atributlar
       — setattr(A, 'y', 1) dan keyin metodlar y ni ko'rardimi?

  ⭐ Python aniq yo'lni tanladi: self.x yoki A.x.
     Bu — Zen ning "Aniq yashirinlikdan yaxshi" tamoyili.
""")


print("\n=== 5. Sinf tanasi — bir marta bajariladi ===")


class H:
    print("      (sinf tanasi bajarilmoqda)")
    x = 1
    print(f"      x = {x}, locals() = {sorted(locals())}")

    def metod(self):
        pass

    print(f"      metod aniqlangandan keyin: {sorted(locals())}")


print(f"\n  H.__dict__ (foydali qismi):")
for k, v in H.__dict__.items():
    if not k.startswith("__"):
        print(f"    {k}: {v}")

print("""
  ⭐ Sinf tanasi:
     1. Alohida nom fazosida bajariladi
     2. Natija (locals()) type() ga uzatiladi
     3. type() sinf obyektini yaratadi

     class A: ...   ≈   A = type("A", (), {...})
""")

A_dinamik = type("A_dinamik", (), {"x": 1, "metod": lambda self: self.x})
print(f"  type() bilan: A_dinamik().metod() = {A_dinamik().metod()}")


print("\n=== 6. Nested sinf va funksiya ===")

z = "GLOBAL_Z"


def tashqi_funksiya():
    z = "ENCLOSING_Z"

    class Ichki:
        z = "SINF_Z"

        def metod(self):
            return z                # ⚠️ ENCLOSING (sinf emas!)

    return Ichki().metod(), Ichki.z


metod_natija, sinf_z = tashqi_funksiya()
print(f"  def tashqi():")
print(f"      z = 'ENCLOSING_Z'")
print(f"      class Ichki:")
print(f"          z = 'SINF_Z'")
print(f"          def metod(self): return z\n")
print(f"    metod() → {metod_natija!r}   ⚠️ ENCLOSING, sinf emas")
print(f"    Ichki.z → {sinf_z!r}")

print("""
  ⭐ LEGB zanjiri: metod → Ichki (O'TKAZIB YUBORILADI) → tashqi_funksiya → global

     Sinf qamrovi zanjirdan CHIQARIB TASHLANGAN.
""")

Natijaning muhim qismi:

text
=== 1. Sinf atributi metodda ko'rinmaydi ===
    a.metod()       → 'GLOBAL'   ⚠️ global topildi
    a.self_bilan()  → 'SINF'   ✅

=== 2. ⚠️ Generatorlar bilan tuzoq ===
  ✅ ikkilangan = [2, 4, 6]
  ❌ juftliklar → NameError: name 'q' is not defined

    [x for x in q if x in q]          ❌ NameError     ikkinchi q — filtr ichida
    [q[i] for i in range(3)]          ❌ NameError     q generator tanasida
    [i for i in range(len(q))]        ✅               len(q) — birinchi for ichida ✅

=== 6. Nested sinf va funksiya ===
    metod() → 'ENCLOSING_Z'   ⚠️ ENCLOSING, sinf emas
    Ichki.z → 'SINF_Z'

Nima ko'rsatdi: 2.4-bo'lim.

Misol 4 — Amaliy: qamrov tahlilchisi

python
"""Qamrov muammolarini topish va tushunish."""

import dis
import inspect
from types import FunctionType

print("=== Qamrov tahlilchisi ===\n")


def qamrov_tahlili(f) -> dict:
    """Funksiyaning qamrov ishlatishini tahlil qiladi."""
    kod = f.__code__
    return {
        "lokal": kod.co_varnames[:kod.co_argcount + kod.co_kwonlyargcount],
        "boshqa_lokal": kod.co_varnames[kod.co_argcount + kod.co_kwonlyargcount:],
        "yopilma": kod.co_freevars,
        "hujayra": kod.co_cellvars,
        "global": kod.co_names,
    }


def qamrov_hisoboti(f):
    """Chiroyli hisobot."""
    t = qamrov_tahlili(f)
    print(f"  {f.__name__}:")
    for nom, qiymat in t.items():
        if qiymat:
            print(f"    {nom:<16} {qiymat}")
    if f.__closure__:
        qiymatlar = {n: c.cell_contents
                     for n, c in zip(f.__code__.co_freevars, f.__closure__)}
        print(f"    {'saqlangan':<16} {qiymatlar}")
    print()


GLOBAL_NOM = "global qiymat"


def oddiy(a, b=2):
    lokal = a + b
    return lokal + len(str(GLOBAL_NOM))


def yopilma_yaratuvchi(n, m):
    def ichki(x):
        return x * n + m
    return ichki


def hujayra_bilan(n):
    def ichki():
        return n
    ichki()
    return n


print("=== 1. Qamrov turlari ===\n")

qamrov_hisoboti(oddiy)
qamrov_hisoboti(yopilma_yaratuvchi(2, 3))
qamrov_hisoboti(hujayra_bilan)


print("=== 2. Bytecode instruksiyalari ===\n")


def aralash(a):
    lokal = 1
    return lokal + a + GLOBAL_NOM_2 + len([1])


GLOBAL_NOM_2 = 10

INSTRUKSIYALAR = {}
for i in dis.get_instructions(aralash):
    if i.opname.startswith("LOAD_"):
        INSTRUKSIYALAR.setdefault(i.opname, []).append(i.argrepr)

print(f"  def aralash(a):")
print(f"      lokal = 1")
print(f"      return lokal + a + GLOBAL_NOM_2 + len([1])\n")

IZOHLAR = {
    "LOAD_FAST": "lokal (massiv indeksi) — ENG TEZ",
    "LOAD_DEREF": "yopilma (cell obyekti) — tez",
    "LOAD_GLOBAL": "global/built-in (lug'at) — sekinroq",
    "LOAD_CONST": "konstanta",
}

print(f"  {'Instruksiya':<16} {'Nomlar':<24} Izoh")
print("  " + "─" * 70)
for opname, nomlar in INSTRUKSIYALAR.items():
    izoh = IZOHLAR.get(opname, "")
    print(f"  {opname:<16} {str(nomlar)[:22]:<24} {izoh}")


print("\n=== 3. Tezlik farqi ===\n")

import timeit

SOZLASH = """
GLOBAL_QIYMAT = 42
r = list(range(1000))

def lokal_bilan(r):
    natija = 0
    lokal = 42
    for x in r:
        natija += x + lokal
    return natija

def global_bilan(r):
    natija = 0
    for x in r:
        natija += x + GLOBAL_QIYMAT
    return natija

def built_in_bilan(r):
    natija = 0
    for x in r:
        natija += len(str(x))
    return natija

def lokalga_kochirilgan(r, _len=len, _str=str):
    natija = 0
    for x in r:
        natija += _len(_str(x))
    return natija
"""

SINOVLAR = [
    ("Lokal o'zgaruvchi",       "lokal_bilan(r)"),
    ("Global o'zgaruvchi",      "global_bilan(r)"),
    ("Built-in (len, str)",     "built_in_bilan(r)"),
    ("Lokalga ko'chirilgan",    "lokalga_kochirilgan(r)"),
]

natijalar = []
for nom, kod in SINOVLAR:
    vaqt = timeit.timeit(kod, setup=SOZLASH, number=2000)
    natijalar.append((nom, vaqt))

eng_tez = min(v for _, v in natijalar)
print(f"  {'Usul':<26} {'Vaqt':>9} {'Nisbat':>9}")
print("  " + "─" * 48)
for nom, vaqt in natijalar:
    print(f"  {nom:<26} {vaqt:>7.3f} s {vaqt / eng_tez:>8.2f}x")

print("""
  ⚠️ Farq kichik (Python 3.11+ LOAD_GLOBAL ni keshlaydi).
     Lokalga ko'chirish — faqat isbotlangan tor joyda.
     O'qilishni qurbon qilmang.
""")


print("\n=== 4. Muammolarni topish ===\n")


def muammolarni_top(manba: str) -> list[str]:
    """Kodda qamrov muammolarini qidiradi."""
    import ast

    muammolar = []
    daraxt = ast.parse(manba)

    for tugun in ast.walk(daraxt):
        # 1. Ichki nomlarni yopish
        if isinstance(tugun, ast.Assign):
            for maqsad in tugun.targets:
                if isinstance(maqsad, ast.Name):
                    import builtins
                    if hasattr(builtins, maqsad.id):
                        muammolar.append(
                            f"{tugun.lineno}: ichki nom yopildi: {maqsad.id!r}"
                        )

        # 2. Sinf tanasida ko'p for li generator
        if isinstance(tugun, ast.ClassDef):
            for element in tugun.body:
                for ichki in ast.walk(element):
                    if isinstance(ichki, (ast.ListComp, ast.SetComp,
                                          ast.DictComp, ast.GeneratorExp)):
                        if len(ichki.generators) > 1:
                            muammolar.append(
                                f"{ichki.lineno}: sinf tanasida ko'p `for` li "
                                f"generator — NameError xavfi"
                            )

        # 3. Siklda lambda (kech bog'lanish)
        if isinstance(tugun, (ast.For, ast.While)):
            for ichki in ast.walk(tugun):
                if isinstance(ichki, ast.Lambda):
                    erkin = {n.id for n in ast.walk(ichki)
                             if isinstance(n, ast.Name)
                             and isinstance(n.ctx, ast.Load)}
                    sikl_ozgaruvchilari = set()
                    if isinstance(tugun, ast.For) and isinstance(tugun.target, ast.Name):
                        sikl_ozgaruvchilari.add(tugun.target.id)
                    if erkin & sikl_ozgaruvchilari:
                        muammolar.append(
                            f"{ichki.lineno}: siklda lambda sikl o'zgaruvchisini "
                            f"ishlatadi — kech bog'lanish"
                        )

    return muammolar


KOD = '''
list = [1, 2, 3]
dict = {}

class A:
    q = [1, 2, 3]
    yaxshi = [x * 2 for x in q]
    yomon = [(x, y) for x in q for y in q]

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

print(f"  Tahlil qilinayotgan kod:")
for i, q in enumerate(KOD.strip().splitlines(), 1):
    print(f"    {i:>2} │ {q}")

print(f"\n  Topilgan muammolar:")
for m in muammolarni_top(KOD):
    print(f"    ⚠️ {m}")


print("\n=== 5. Qamrov qoidalari xulosasi ===\n")

QOIDALAR = [
    ("LEGB tartibi",
     "Local → Enclosing → Global → Built-in"),
    ("Lokal aniqlanishi",
     "KOMPILYATSIYA paytida: tanada o'zlashtirish bo'lsa — lokal"),
    ("O'zlashtirish shakllari",
     "=, +=, for, with as, except as, import, def, class, :type, :=, del"),
    ("UnboundLocalError",
     "Lokal, lekin hali qiymat olmagan"),
    ("Yopilma",
     "Ichki funksiya tashqi o'zgaruvchini eslab qoladi (__closure__)"),
    ("Yopilmani o'zgartirish",
     "nonlocal kerak (9-dars)"),
    ("Sinf qamrovi",
     "LEGB zanjirida QATNASHMAYDI — self.x yoki A.x"),
    ("Qamrov yaratmaydi",
     "for, while, if, with, try"),
    ("except as x",
     "Blokdan keyin O'CHIRILADI"),
    ("locals()",
     "Funksiyada NUSXA — o'zgartirish ishlamaydi"),
    ("globals()",
     "Haqiqiy lug'at — o'zgartirish ishlaydi"),
]

for nom, izoh in QOIDALAR:
    print(f"  {nom:<26} {izoh}")

Natijaning muhim qismi:

text
=== 1. Qamrov turlari ===

  oddiy:
    lokal            ('a', 'b')
    boshqa_lokal     ('lokal',)
    global           ('len', 'str', 'GLOBAL_NOM')

  ichki:
    lokal            ('x',)
    yopilma          ('m', 'n')
    saqlangan        {'m': 3, 'n': 2}

  hujayra_bilan:
    lokal            ('n',)
    boshqa_lokal     ('ichki',)
    hujayra          ('n',)

=== 4. Muammolarni topish ===

  Topilgan muammolar:
    ⚠️ 2: ichki nom yopildi: 'list'
    ⚠️ 3: ichki nom yopildi: 'dict'
    ⚠️ 8: sinf tanasida ko'p `for` li generator — NameError xavfi
    ⚠️ 12: siklda lambda sikl o'zgaruvchisini ishlatadi — kech bog'lanish

Nima ko'rsatdi: 2.2, 2.4, 2.6-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Lokal/global ish vaqtida aniqlanadi" Kompilyatsiya paytida
"print(x) global x ni o'qiydi" Agar tanada x = ... bo'lsa — UnboundLocalError
"for alohida qamrov yaratadi" Yaratmaydi
"except as x dan keyin x qoladi" O'chiriladi
"Sinf atributi metodda ko'rinadi" self.x kerak
"Generatorda sinf atributlari ko'rinadi" Faqat birinchi for da
"locals() ni o'zgartirish ishlaydi" Funksiyada — yo'q
"Yopilma qiymat nusxasini saqlaydi" Havola saqlaydi (kech bog'lanish)
"del x global x ni o'chiradi" Funksiyada — x ni lokal qiladi

6. Keng tarqalgan xatolar va yechimlari

1. UnboundLocalError

python
x = 10
def f():
    print(x)                        # ❌
    x = 20

def f():
    global x                        # ✅ (9-dars)
    print(x)
    x = 20

def f(x_arg=x):                     # ✅ argument sifatida
    print(x_arg)

2. Ichki nomni yopish

python
list = [1, 2]                       # ❌
royxat = [1, 2]                     # ✅
list_ = [1, 2]                      # ✅

3. Sinf tanasida ko'p for li generator

python
class A:
    q = [1, 2]
    r = [(x, y) for x in q for y in q]       # ❌

Q = [1, 2]                                    # ✅
class A:
    q = Q
    r = [(x, y) for x in Q for y in Q]

4. Kech bog'lanish

python
[lambda: i for i in range(3)]       # ⚠️ [2, 2, 2]
[lambda i=i: i for i in range(3)]   # ✅ [0, 1, 2]

5. Yopilmani o'zgartirish

python
def tashqi():
    x = 1
    def ichki():
        x = 2                       # ⚠️ yangi lokal
    ...

def tashqi():
    x = 1
    def ichki():
        nonlocal x                  # ✅
        x = 2

6. except as dan keyin ishlatish

python
try: ...
except E as x: ...
print(x)                            # ❌ NameError

xato = None
try: ...
except E as x: xato = x             # ✅

7. locals() ni o'zgartirish

python
def f():
    locals()["x"] = 1               # ⚠️ ishlamaydi
    return x                        # ❌ NameError

8. Metodda sinf atributi

python
class A:
    x = 1
    def m(self): return x           # ⚠️ global
    def m(self): return self.x      # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 7.9-dars: global va nonlocal
  • 7.10-dars: lambda va yopilmalar
  • 6.15-dars (o'tilgan): generator qamrovi, kech bog'lanish
  • 8-qism: sinflar, self, atributlar
  • 10-qism: dekoratorlar (yopilmalar asosida)
  • 19-qism: modullar, import va nom fazolari
  • dis, inspect: bytecode va qamrov tahlili

8. Eng yaxshi amaliyotlar

  1. Global o'zgaruvchilardan qoching. Argument sifatida uzating.

  2. Ichki nomlarni yopmang. list_, id_, type_.

  3. Tanada nomga o'zlashtirish bo'lsa — u lokal. UnboundLocalError sababi shu.

  4. Sinf atributiga self.x yoki A.x. Bevosita nom ishlamaydi.

  5. Siklda lambda — sukut argument bilan. lambda i=i: i.

  6. except as x natijasini boshqa nomga saqlang.

  7. locals() ni faqat o'qish uchun. O'zgartirish ishlamaydi.

  8. Yopilma o'zgaruvchisini o'zgartirish uchun nonlocal. O'zgaruvchan obyekt hiylasi emas.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
x = "global"

1.  def f(): return x
    print(f())
2.  def f():
        x = "lokal"
        return x
    print(f(), x)
3.  def f():
        print(x)
        x = 1
    f()
4.  def t():
        x = "enc"
        def i(): return x
        return i()
    print(t())
5.  def t():
        x = "enc"
        def i(): x = "in"
        i()
        return x
    print(t())
6.  class A:
        x = "sinf"
        def m(self): return x
    print(A().m())
7.  for i in range(3): pass
    print(i)
8.  try: raise ValueError
    except ValueError as e: pass
    print(e)
9.  y = 10
    [y for y in range(3)]
    print(y)
10. def f():
        locals()["z"] = 1
        return z
    f()
11. def f():
        del x
    f()
12. print(len([1]) == (lambda: len)()([1]))
Javoblar
  1. global
  2. lokal global
  3. UnboundLocalError
  4. enc
  5. enc — ichki x alohida
  6. global — sinf qamrovi ko'rinmaydi
  7. 2 — for qamrov yaratmaydi
  8. NameError — except as o'chiriladi
  9. 10 — generator o'z qamrovi
  10. NameError — locals() nusxa
  11. UnboundLocalError — del ham lokal qiladi
  12. True

Vazifa 2: Xatolarni tuzating

python
1.  sanoq = 0
    def qosh():
        sanoq += 1
2.  list = [1, 2, 3]
3.  class A:
        q = [1, 2]
        r = [(x,y) for x in q for y in q]
4.  fs = [lambda: i for i in range(3)]
5.  try: ...
    except E as x: ...
    print(x)
6.  def f():
        locals()["x"] = 1
        return x
7.  class A:
        x = 1
        def m(self): return x
8.  def t():
        x = 1
        def i(): x = 2
        i(); return x       # 2 kutilyapti
Javoblar
python
1.  global sanoq   (yoki argument/qaytarish)
2.  royxat = [1, 2, 3]
3.  Q = [1, 2] modul darajasida, keyin [(x,y) for x in Q for y in Q]
4.  fs = [lambda i=i: i for i in range(3)]
5.  xato = None; except E as x: xato = x
6.  def f(): x = 1; return x
7.  def m(self): return self.x
8.  def i(): nonlocal x; x = 2

Vazifa 3: Qamrov tahlilchisi

Vosita yozing:

  1. Funksiya uchun barcha qamrovlarni ko'rsatsin
  2. Har nom qaysi qamrovdan kelayotganini
  3. dis bilan LOAD_* instruksiyalarini
  4. Yopilma qiymatlarini
  5. Muammolarni topsin (ichki nom yopish, kech bog'lanish)
  6. Grafik ko'rinishda chiqarsin

Vazifa 4: Yopilmalar kutubxonasi

Yozing:

  1. sanagich(boshlang'ich=0, qadam=1) — hisoblagich
  2. kesh(f) — sodda memoizatsiya
  3. chegaralangan(f, n) — n martadan ko'p chaqirilmaydi
  4. bir_marta(f) — faqat bir marta bajariladi
  5. kechiktirilgan(f, *args) — keyinroq chaqirish uchun
  6. Har biri uchun __closure__ ni ko'rsating

Vazifa 5: Kech bog'lanish tadqiqoti

Dastur yozing:

  1. Kech bog'lanish muammosini 5 xil kontekstda ko'rsating
  2. Har biri uchun 3 xil yechim
  3. __closure__ farqini ko'rsating
  4. Tezlikni solishtiring
  5. Qaysi yechim qachon yaxshiroq

Vazifa 6: Sinf qamrovi tadqiqoti

  1. Sinf tanasida nima ishlaydi, nima yo'q — to'liq jadval
  2. Har buziladigan holat uchun 2 yechim
  3. type() bilan dinamik sinf yaratish
  4. Metaclass bilan farq
  5. dataclass, NamedTuple da qanday

Vazifa 7: O'ylash

Nega Python lokal o'zgaruvchini kompilyatsiya paytida aniqlaydi, ish vaqtida emas?

Javob

Sabab: tezlik.

Agar ish vaqtida aniqlansa:

python
def f():
    return x

Har x uchun:

  1. Lokal lug'atda qidirish
  2. Topilmasa — enclosing
  3. Topilmasa — global
  4. Topilmasa — built-in

To'rt lug'at qidiruvi, har murojaatda.

Kompilyatsiya paytida aniqlanganda:

python
def f():
    x = 1
    return x        # LOAD_FAST 0  ← massiv indeksi

x — co_varnames dagi 0-indeks. Ish vaqtida:

c
value = frame->localsplus[0];       // massiv elementi

Bitta massiv murojaati — lug'at qidiruvidan ~5-10x tez.

Raqamlar:

python
def lokal():
    x = 1
    for _ in range(1000):
        y = x               # LOAD_FAST

def global_():
    for _ in range(1000):
        y = GLOBAL_X        # LOAD_GLOBAL

LOAD_FAST ~2-3x tezroq (Python 3.11+ da farq kichraydi — LOAD_GLOBAL keshlanadi).

Funksiya chaqiruvi — eng ko'p bajariladigan amal. Uni tezlashtirish butun tilni tezlashtiradi.

Narxi:

1. UnboundLocalError — chalkash xato:

python
x = 10
def f():
    print(x)                # ❌
    x = 20

Yangi boshlovchilar uchun bu — tuzoq. Xato xabari yaxshilangan (Python 3.11+):

UnboundLocalError: cannot access local variable 'x' where it is not
associated with a value

2. Dinamik nom yaratish ishlamaydi:

python
def f():
    locals()["x"] = 1
    return x                # ❌ NameError

x co_varnames da yo'q — kompilyator uni ko'rmagan.

3. exec funksiya ichida cheklangan:

python
def f():
    exec("x = 1")
    return x                # ❌ NameError

exec locals() nusxasini o'zgartiradi, haqiqiy massivni emas.

Boshqa tillar:

Til Qamrov aniqlanishi
Python Kompilyatsiya paytida
JavaScript (let/const) Kompilyatsiya paytida (TDZ)
JavaScript (var) Kompilyatsiya (hoisting)
Ruby Kompilyatsiya paytida
PHP Ish vaqtida (dinamik)
Lua Kompilyatsiya paytida

JavaScript qiziq:

javascript
function f() {
    console.log(x);         // ❌ ReferenceError (TDZ)
    let x = 20;
}

Bu — Python bilan bir xil muammo. JavaScript uni "Temporal Dead Zone" deb ataydi.

var bilan esa:

javascript
function f() {
    console.log(x);         // undefined (xato yo'q)
    var x = 20;
}

Bu — yomonroq: xato yo'q, lekin undefined. Python UnboundLocalError bilan aniqroq.

Muqobil dizayn bo'lardimi?

Variant A: Global deb hisoblash, keyin lokal

python
x = 10
def f():
    print(x)                # 10
    x = 20                  # endi lokal
    print(x)                # 20

Muammo: bir nom ikki ma'noga ega. Kod kuzatish qiyin. Va tezlik yo'qoladi.

Variant B: Aniq e'lon (var x)

python
def f():
    var x                   # aniq lokal
    print(x)

Muammo: Pythonning "e'lon yo'q" falsafasiga zid. Kod uzunroq.

Variant C: Hozirgi (Python tanlagan)

Bir qoida: "tanada o'zlashtirish bo'lsa — lokal". Sodda va tez.

Xulosa: bu — tezlik uchun to'langan narx. Qoida bir jumlada tushuntiriladi, va global/nonlocal bilan hal qilinadi. Xato xabari ham aniq. Amalda muammo faqat yangi boshlovchilarda va bir marta uchraydi — keyin qoida o'zlashadi.

Nimani mustahkamlaydi: 2.1, 2.2, 2.6-bo'limlar.


Xulosa

Bu darsda LEGB qoidasini o'rgandik.

Eng muhim uch fikr:

  1. LEGB: Local → Enclosing → Global → Built-in. Python nomni shu tartibda qidiradi. Topilmasa NameError.

  2. Lokal/global kompilyatsiya paytida aniqlanadi. Funksiya tanasida nomga o'zlashtirish bo'lsa (=, +=, for, with as, import, hatto del), u butun funksiya davomida lokal. Shuning uchun print(x) dan keyin x = 1 yozilsa — UnboundLocalError.

  3. Sinf qamrovi LEGB zanjirida qatnashmaydi. Metod ichidan sinf atributiga faqat self.x yoki A.x orqali. Va sinf tanasidagi ko'p for li generator NameError beradi — chunki generator yashirin funksiya, undan sinf qamrovi ko'rinmaydi.

Keyingi darsda global va nonlocal ni ko'ramiz: qamrov qoidasini ataylab buzish, qachon kerak va qachon yomon dizayn belgisi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
7.8-dars: LEGB — ko'rinish sohasi (scope) — IlmHamroh