IlmHamroh
Python kursi/Funksiyalar9/14-dars36 daqiqa
Mundarija (21)

7.9-dars: global va nonlocal

7-QISM — FUNKSIYALAR · 9-dars


1. Kirish va motivatsiya

8-darsda ko'rdik: funksiya tanasida nomga o'zlashtirish bo'lsa — u lokal.

python
sanoq = 0

def qosh():
    sanoq += 1              # ❌ UnboundLocalError

qosh()

Va yopilmada ham:

python
def sanagich():
    hisob = 0
    def ichki():
        hisob += 1          # ❌ UnboundLocalError
        return hisob
    return ichki

Ikki kalit so'z bu qoidani ataylab buzadi:

python
sanoq = 0

def qosh():
    global sanoq            # ⭐ "sanoq — modul darajasida"
    sanoq += 1


def sanagich():
    hisob = 0
    def ichki():
        nonlocal hisob      # ⭐ "hisob — tashqi funksiyada"
        hisob += 1
        return hisob
    return ichki

Lekin ular — oxirgi chora. Ko'p holatda global holat yomon dizayn belgisi:

python
# ❌ Global holat
jami = 0
def qosh(x):
    global jami
    jami += x

# ✅ Qaytarish
def qosh(jami, x):
    return jami + x

# ✅ Sinf
class Hisoblagich:
    def __init__(self): self.jami = 0
    def qosh(self, x): self.jami += x

Bu darsda:

  • global — modul qamroviga yozish
  • nonlocal — enclosing qamroviga yozish
  • Qachon kerak, qachon yomon dizayn
  • Muqobillar: qaytarish, sinf, itertools.count
  • Ko'p oqimli kod va global holat
  • Modullar orasida global o'zgaruvchilar

2. Nazariya — chuqur tushuntirish

2.1. global

python
sanoq = 0

def qosh():
    global sanoq            # "sanoq lokal EMAS, modul darajasida"
    sanoq += 1

qosh()
print(sanoq)                # 1

global nima qiladi:

Kompilyatorga aytadi: bu nom lokal emas, uni modul nom fazosida qidir va yoz.

Bytecode:

python
def globalsiz():
    x = 1                   # STORE_FAST

def global_bilan():
    global x
    x = 1                   # STORE_GLOBAL

O'qish uchun global KERAK EMAS:

python
x = 10

def f():
    return x                # ✅ global topiladi

def g():
    global x                # ⚠️ ortiqcha
    return x

global faqat yozish uchun kerak.

Yangi global yaratish:

python
def f():
    global yangi
    yangi = 42

f()
print(yangi)                # 42  ✅

Bir necha nom:

python
global a, b, c

global funksiya boshida bo'lishi shart emas, lekin:

python
def f():
    x = 1                   # lokal
    global x                # ❌ SyntaxError: name 'x' is assigned to before
                            #    global declaration

E'lon birinchi ishlatishdan oldin bo'lishi kerak.

global argumentga qo'llanmaydi:

python
def f(x):
    global x                # ❌ SyntaxError: name 'x' is parameter and global

Modul darajasida global — ma'nosiz:

python
global x                    # ⚠️ ishlaydi, lekin hech narsa qilmaydi
x = 1

2.2. nonlocal

Python 3.0+ (PEP 3104). Enclosing qamrovdagi o'zgaruvchini o'zgartiradi:

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

tashqi()                    # 2

nonlocal va global farqi:

global nonlocal
Qamrov Modul (G) Eng yaqin enclosing (E)
Nom mavjud bo'lishi Shart emas (yaratadi) Shart
Modul darajasida Ma'nosiz SyntaxError
Global ni topadi Yo'q

Nom enclosing da mavjud bo'lishi SHART:

python
def tashqi():
    def ichki():
        nonlocal x          # ❌ SyntaxError: no binding for nonlocal 'x' found
        x = 1
    ichki()

nonlocal global ni topmaydi:

python
x = 10

def f():
    nonlocal x              # ❌ SyntaxError
    x = 20

Global uchun global kerak.

Eng yaqin enclosing:

python
def a():
    x = "a"
    def b():
        x = "b"
        def c():
            nonlocal x      # ⭐ b ning x i (a niki emas)
            x = "c"
        c()
        return x            # "c"
    return b(), x           # ("c", "a")

Bir necha daraja yuqoriga:

python
def a():
    x = "a"
    def b():
        def c():
            nonlocal x      # ⭐ a ning x i (b da x yo'q)
            x = "c"
        c()
    b()
    return x                # "c"

nonlocal eng yaqin x bor bo'lgan enclosing qamrovni topadi.

2.3. Qachon kerak, qachon yomon

Global holat — kamdan-kam to'g'ri.

Muammolar:

  1. Test qilish qiyin — holatni tiklash kerak
  2. Ko'p oqimda xavfli — poyga holati (race condition)
  3. Bog'liqlik yashirin — funksiya imzosida ko'rinmaydi
  4. Qayta ishlatib bo'lmaydi — bitta global holat
  5. Kuzatish qiyin — kim o'zgartirdi?

Muqobillar:

python
# ❌ Global
jami = 0
def qosh(x):
    global jami
    jami += x

# ✅ 1. Qaytarish (toza funksiya)
def qosh(jami, x):
    return jami + x

# ✅ 2. Sinf (holat obyektda)
class Hisoblagich:
    def __init__(self):
        self.jami = 0
    def qosh(self, x):
        self.jami += x
        return self.jami

# ✅ 3. Yopilma (nonlocal bilan)
def hisoblagich_yarat():
    jami = 0
    def qosh(x):
        nonlocal jami
        jami += x
        return jami
    return qosh

# ✅ 4. Tayyor vosita
from itertools import count
sanagich = count()
next(sanagich)

Qachon global maqbul:

Holat Izoh
Modul konstantalari _KESH, _SOZLAMALAR — kirish nuqtalari orqali
Singleton (bir marta ishga tushirish) _ulanish
Kesh (functools ichida ham shunday) _KESH = {}
Skript (kichik, bir martalik) Prototip uchun
Legacy kod bilan integratsiya Boshqa yo'l yo'q

Qachon nonlocal maqbul:

Holat Izoh
Dekoratorlarda hisoblagich chaqiruvlar += 1
Yopilma bilan holat Kichik, mahalliy
Callback'da natija to'plash GUI, hodisa tizimlari
Rekursiv funksiyada akkumulyator Ba'zan nonlocal aniqroq

Lekin ko'pincha sinf yaxshiroq:

python
# nonlocal bilan
def sanagich():
    hisob = 0
    def ichki():
        nonlocal hisob
        hisob += 1
        return hisob
    ichki.tozala = lambda: None     # ⚠️ hisob ni tozalab bo'lmaydi
    return ichki

# Sinf bilan — to'liq nazorat
class Sanagich:
    def __init__(self):
        self.hisob = 0
    def __call__(self):
        self.hisob += 1
        return self.hisob
    def tozala(self):
        self.hisob = 0

2.4. Modul darajasidagi holat

Modul — singleton. U bir marta import qilinadi:

python
# sozlamalar.py
_KESH = {}
_ULANISH = None


def ulanish_ol():
    global _ULANISH
    if _ULANISH is None:
        _ULANISH = ulan()           # bir marta
    return _ULANISH


def keshga_yoz(kalit, qiymat):
    _KESH[kalit] = qiymat           # ⭐ global KERAK EMAS — o'zgartirish

Muhim farq:

python
_KESH = {}

def f():
    _KESH["a"] = 1                  # ✅ global kerak EMAS (obyektni o'zgartirish)

def g():
    global _KESH
    _KESH = {}                      # ✅ global KERAK (qayta bog'lash)

Bu — 7.2-darsdagi "o'zgartirish vs qayta bog'lash" farqi.

from modul import x — nusxa oladi:

python
# a.py
x = 1
def ozgartir():
    global x
    x = 2

# b.py
from a import x, ozgartir
ozgartir()
print(x)                            # 1  ⚠️ o'zgarmadi!

import a
a.ozgartir()
print(a.x)                          # 2  ✅

from a import x — x ning joriy qiymatiga yangi nom bog'laydi. Keyingi o'zgarishlar ko'rinmaydi.

Shuning uchun o'zgaruvchan global holat uchun import modul ishlating.

2.5. Ko'p oqimli kod

Global holat — poyga holati (race condition):

python
import threading

sanoq = 0

def qosh():
    global sanoq
    for _ in range(100_000):
        sanoq += 1                  # ⚠️ atomik EMAS

oqimlar = [threading.Thread(target=qosh) for _ in range(2)]
for o in oqimlar: o.start()
for o in oqimlar: o.join()

print(sanoq)                        # ⚠️ 200000 bo'lishi KAFOLATLANMAGAN

Bu kodni hozirgi CPython (3.10+) da ishga tushirsangiz, ehtimol 200000 chiqadi — va "demak xavfsiz" degan xulosaga kelish oson. Bu xato xulosa:

  • sanoq += 1 baribir uch qadam: o'qish → hisoblash → yozish.
  • GIL iplarni odatda aynan shu qadamlar orasida almashtirmaydi, shuning uchun yo'qotish kamdan-kam ko'rinadi — lekin bu til kafolati emas, CPython'ning ichki tafsiloti.
  • Free-threaded Python (3.13t va undan keyingi, GIL'siz) da, PyPy va boshqa implementatsiyalarda qiymatlar haqiqatan yo'qoladi.
  • O'qish va yozish orasida funksiya chaqiruvi, I/O yoki sleep bo'lsa (real kodda deyarli doim shunday), yo'qotish CPython'da ham darhol ko'rinadi (4-misol).

Qoida: umumiy holat o'zgarsa — threading.Lock, queue.Queue yoki global holatdan voz kechish.

Sabab: sanoq += 1 uch amaldan iborat:

LOAD_GLOBAL sanoq
ADD 1
STORE_GLOBAL sanoq

Oqim ular orasida uzilishi mumkin.

Yechimlar:

python
# 1. Lock
qulf = threading.Lock()

def qosh():
    global sanoq
    for _ in range(100_000):
        with qulf:
            sanoq += 1              # ✅ atomik

# 2. threading.local — har oqimga o'z holati
lokal = threading.local()

def qosh():
    lokal.sanoq = getattr(lokal, "sanoq", 0) + 1

# 3. Queue — oqimlar orasida ma'lumot
from queue import Queue
natijalar = Queue()

# 4. Global holatdan umuman voz kechish ⭐
def qosh(n):
    return sum(1 for _ in range(n))

GIL global holatni himoya QILMAYDI:

GIL bir vaqtda bitta bytecode instruksiyasini kafolatlaydi, lekin += — bir nechta instruksiya (o'qish → hisoblash → yozish).

2.6. Nozikliklar

1. global tanani skanerlashda ishlaydi:

python
def f():
    if False:
        global x                    # ⚠️ shart bajarilmasa ham ta'sir qiladi
    x = 1                           # global x

f()
print(x)                            # 1

Kompilyator butun tanani ko'radi — shart ahamiyatsiz.

2. global ichma-ich funksiyada:

python
def tashqi():
    def ichki():
        global x                    # ⭐ MODUL darajasi, tashqi emas
        x = 1
    ichki()

tashqi()
print(x)                            # 1

3. nonlocal + global birga:

python
def f():
    x = 1
    def g():
        nonlocal x
        global y
        x = 2
        y = 3
    g()
    return x

4. Sinf tanasida:

python
x = 1

class A:
    global x
    x = 2                           # ⚠️ modulning x i o'zgardi

print(x)                            # 2
print(A.x)                          # ❌ AttributeError

Kamdan-kam ishlatiladi va chalkash.

5. exec bilan:

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

def f():
    exec("global x; x = 1", globals())
    return globals()["x"]           # ✅

6. Lambda da ishlatib bo'lmaydi:

python
f = lambda: global x                # ❌ SyntaxError

lambda faqat ifoda, global — buyruq.

2.7. Muqobil vositalar

itertools.count — hisoblagich:

python
from itertools import count

sanagich = count(1)
next(sanagich)                      # 1
next(sanagich)                      # 2

functools.lru_cache — kesh:

python
@lru_cache(maxsize=128)
def qimmat(n): ...

contextvars — kontekstga bog'liq holat (async uchun):

python
from contextvars import ContextVar

joriy_foydalanuvchi = ContextVar("foydalanuvchi", default=None)
joriy_foydalanuvchi.set("aziz")
joriy_foydalanuvchi.get()

threading.local — oqimga bog'liq holat:

python
import threading
lokal = threading.local()
lokal.malumot = "har oqimga alohida"

Funksiya atributi:

python
def f():
    f.chaqiruvlar += 1
    return f.chaqiruvlar
f.chaqiruvlar = 0

dataclass — holat konteyneri:

python
@dataclass
class Holat:
    sanoq: int = 0
    kesh: dict = field(default_factory=dict)

3. Tez ma'lumotnoma

global

python
global x        "x — MODUL darajasida"

O'qish uchun KERAK EMAS
Yozish (qayta bog'lash) uchun KERAK
Yangi global yaratadi
Bir necha nom: global a, b, c

❌ def f(x): global x           parametr
❌ x = 1; global x              o'zlashtirishdan keyin

nonlocal

python
nonlocal x      "x — ENG YAQIN enclosing qamrovda"

⚠️ Nom mavjud bo'lishi SHART
❌ Modul darajasida — SyntaxError
❌ Global ni TOPMAYDI

Farq

              global          nonlocal
Qamrov        modul (G)       enclosing (E)
Mavjudlik     shart emas      SHART
Yaratadi      ✅              ❌

O'zgartirish vs qayta bog'lash

python
_KESH = {}
def f(): _KESH["a"] = 1     ✅ global KERAK EMAS
def g(): global _KESH; _KESH = {}   ✅ global KERAK

from import

python
from a import x             nusxa — keyingi o'zgarish KO'RINMAYDI
import a; a.x               ✅ har doim joriy qiymat

Muqobillar

Qaytarish (toza funksiya)   ⭐ eng yaxshi
Sinf                        holat + metodlar
Yopilma + nonlocal          kichik, mahalliy
itertools.count             hisoblagich
functools.lru_cache         kesh
threading.local             oqimga bog'liq
contextvars                 async kontekst
Funksiya atributi           f.hisob

Ko'p oqim

python
sanoq += 1      ATOMIK EMAS (o'qish → hisoblash → yozish) → race condition
→ threading.Lock, Queue, yoki global holatdan voz kechish

4. Batafsil misollar

Misol 1 — global

python
"""global — modul qamroviga yozish."""

import dis

print("=== 1. Muammo va yechim ===")

sanoq = 0


def qosh_yomon():
    sanoq += 1                      # ⚠️


def qosh_yaxshi():
    global sanoq
    sanoq += 1


print(f"  sanoq = {sanoq}\n")
print(f"  def qosh(): sanoq += 1")
try:
    qosh_yomon()
except UnboundLocalError as e:
    print(f"    → ❌ UnboundLocalError: {e}\n")

print(f"  def qosh():")
print(f"      global sanoq")
print(f"      sanoq += 1")
qosh_yaxshi()
qosh_yaxshi()
print(f"    → ✅ sanoq = {sanoq}")


print("\n=== 2. O'qish uchun kerak emas ===")

x = "GLOBAL"


def faqat_oqish():
    return x                        # ✅ global kerak emas


def ortiqcha_global():
    global x                        # ⚠️ ortiqcha
    return x


print(f"  x = {x!r}\n")
print(f"  def f(): return x                    → {faqat_oqish()!r}  ✅")
print(f"  def f(): global x; return x          → {ortiqcha_global()!r}  ⚠️ ortiqcha")

print(f"\n  Bytecode:")
for f, nom in [(faqat_oqish, "global siz"), (ortiqcha_global, "global bilan")]:
    ins = [i.opname for i in dis.get_instructions(f) if "LOAD" in i.opname]
    print(f"    {nom:<16} {ins}")

print(f"\n  ⭐ Ikkalasi ham LOAD_GLOBAL — farq yo'q")


print("\n=== 3. Yozish uchun kerak ===")


def yozish_global_siz():
    global_test = "lokal"           # yangi lokal
    return global_test


global_test = "GLOBAL"


def yozish_global_bilan():
    global global_test
    global_test = "o'zgartirildi"


print(f"  global_test = {global_test!r}\n")

yozish_global_siz()
print(f"  global siz yozish  → global_test = {global_test!r}  (o'zgarmadi)")

yozish_global_bilan()
print(f"  global bilan yozish → global_test = {global_test!r}  ✅")

print(f"\n  Bytecode:")
for f, nom in [(yozish_global_siz, "global siz"),
               (yozish_global_bilan, "global bilan")]:
    ins = [i.opname for i in dis.get_instructions(f) if "STORE" in i.opname]
    print(f"    {nom:<16} {ins}")


print("\n=== 4. ⚠️ O'zgartirish vs qayta bog'lash ===")

_KESH = {"boshlangich": 1}
_ROYXAT = [1, 2]


def ozgartirish():
    """✅ global KERAK EMAS — obyektni o'zgartiradi."""
    _KESH["yangi"] = 2
    _ROYXAT.append(3)


def qayta_boglash_siz():
    """⚠️ Yangi lokal yaratadi."""
    _KESH = {}
    _ROYXAT = []


def qayta_boglash_bilan():
    """✅ global KERAK."""
    global _KESH, _ROYXAT
    _KESH = {"tozalandi": True}
    _ROYXAT = []


print(f"  Boshlang'ich: _KESH={_KESH}, _ROYXAT={_ROYXAT}\n")

ozgartirish()
print(f"  Obyektni o'zgartirish (global siz):")
print(f"    _KESH={_KESH}, _ROYXAT={_ROYXAT}   ✅ o'zgardi")

qayta_boglash_siz()
print(f"\n  Qayta bog'lash (global siz):")
print(f"    _KESH={_KESH}, _ROYXAT={_ROYXAT}   ⚠️ o'zgarmadi")

qayta_boglash_bilan()
print(f"\n  Qayta bog'lash (global bilan):")
print(f"    _KESH={_KESH}, _ROYXAT={_ROYXAT}   ✅ o'zgardi")

print("""
  ⭐ Qoida (7.2-dars):
     d[k] = v, r.append(x)  → OBYEKTNI o'zgartirish → global kerak EMAS
     d = {}, r = []         → QAYTA BOG'LASH → global KERAK
""")


print("\n=== 5. Yangi global yaratish ===")


def yangi_yarat():
    global yangi_nom
    yangi_nom = "yaratildi"


print(f"  'yangi_nom' in globals(): {'yangi_nom' in globals()}")
yangi_yarat()
print(f"  yangi_yarat() dan keyin:  {'yangi_nom' in globals()}")
print(f"  yangi_nom = {yangi_nom!r}")

del globals()["yangi_nom"]


print("\n=== 6. Sintaksis cheklovlari ===")

XATOLAR = [
    ("Parametr bilan",      "def f(x):\n    global x\n    x = 1"),
    ("O'zlashtirishdan keyin", "def f():\n    x = 1\n    global x"),
    ("lambda da",           "f = lambda: global x"),
    ("nonlocal modulda",    "nonlocal x"),
]

for nom, kod in XATOLAR:
    try:
        exec(compile(kod, "<t>", "exec"))
        natija = "✅ ishladi"
    except SyntaxError as e:
        natija = f"❌ SyntaxError: {e.msg}"
    print(f"  {nom:<26} {natija}")

print(f"\n  ✅ To'g'ri shakllar:")
TOGRI = [
    "def f():\n    global x\n    x = 1",
    "def f():\n    global a, b, c\n    a = b = c = 1",
    "def f():\n    global x\n    return x",
    "def f():\n    if False:\n        global x\n    x = 1",
]
for kod in TOGRI:
    try:
        exec(compile(kod, "<t>", "exec"))
        print(f"    ✅ {kod.splitlines()[1].strip()}")
    except SyntaxError as e:
        print(f"    ❌ {e.msg}")


print("\n=== 7. ⚠️ Shart ichida ham ta'sir qiladi ===")


def shartli_global():
    if False:                       # ⚠️ hech qachon bajarilmaydi
        global shart_test
    shart_test = "o'rnatildi"       # lekin bu GLOBAL


shartli_global()
print(f"  def f():")
print(f"      if False:")
print(f"          global shart_test")
print(f"      shart_test = 'o`rnatildi'\n")
print(f"    'shart_test' in globals(): {'shart_test' in globals()}   ⚠️")
print(f"    shart_test = {shart_test!r}")

print(f"\n  ⭐ Kompilyator butun tanani skanerlaydi — shart ahamiyatsiz")

del globals()["shart_test"]


print("\n=== 8. Ichma-ich funksiyada ===")


def tashqi():
    x = "ENCLOSING"

    def ichki():
        global x                    # ⭐ MODUL, enclosing emas
        x = "GLOBAL_dan_yozildi"

    ichki()
    return x                        # ENCLOSING o'zgarmadi


x = "asl global"
natija = tashqi()

print(f"  def tashqi():")
print(f"      x = 'ENCLOSING'")
print(f"      def ichki():")
print(f"          global x         # ⭐ MODUL darajasi")
print(f"          x = '...'")
print(f"      return x\n")
print(f"    tashqi() qaytardi: {natija!r}   (enclosing o'zgarmadi)")
print(f"    global x:          {x!r}   ✅ o'zgardi")

Natijaning muhim qismi:

text
=== 4. ⚠️ O'zgartirish vs qayta bog'lash ===
  Obyektni o'zgartirish (global siz):
    _KESH={'boshlangich': 1, 'yangi': 2}, _ROYXAT=[1, 2, 3]   ✅ o'zgardi

  Qayta bog'lash (global siz):
    _KESH={'boshlangich': 1, 'yangi': 2}, _ROYXAT=[1, 2, 3]   ⚠️ o'zgarmadi

  Qayta bog'lash (global bilan):
    _KESH={'tozalandi': True}, _ROYXAT=[]   ✅ o'zgardi

=== 6. Sintaksis cheklovlari ===
  Parametr bilan             ❌ SyntaxError: name 'x' is parameter and global
  O'zlashtirishdan keyin     ❌ SyntaxError: name 'x' is assigned to before
                                global declaration
  lambda da                  ❌ SyntaxError: invalid syntax

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

Misol 2 — nonlocal

python
"""nonlocal — enclosing qamroviga yozish."""

print("=== 1. Muammo va yechim ===")


def sanagich_yomon():
    hisob = 0

    def ichki():
        hisob += 1                  # ⚠️
        return hisob

    return ichki


def sanagich_yaxshi():
    hisob = 0

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

    return ichki


print(f"  def sanagich():")
print(f"      hisob = 0")
print(f"      def ichki():")
print(f"          hisob += 1")
print(f"      return ichki\n")

f = sanagich_yomon()
try:
    f()
except UnboundLocalError as e:
    print(f"    → ❌ UnboundLocalError: {e}\n")

print(f"  nonlocal bilan:")
g = sanagich_yaxshi()
print(f"    g() → {g()}, {g()}, {g()}   ✅")


print("\n=== 2. ⭐ global vs nonlocal ===")

x = "GLOBAL"


def demo():
    x = "ENCLOSING"

    def global_bilan():
        global x
        x = "global_dan"

    def nonlocal_bilan():
        nonlocal x
        x = "nonlocal_dan"

    def lokal():
        x = "lokal"
        return x

    natijalar = {}
    lokal_natija = lokal()
    natijalar["lokal dan keyin enclosing"] = x

    nonlocal_bilan()
    natijalar["nonlocal dan keyin enclosing"] = x

    global_bilan()
    natijalar["global dan keyin enclosing"] = x

    return natijalar


natijalar = demo()

print(f"  Boshida: global x = 'GLOBAL', enclosing x = 'ENCLOSING'\n")
for nom, qiymat in natijalar.items():
    print(f"    {nom:<34} {qiymat!r}")
print(f"    {'Modul darajasidagi x':<34} {x!r}")

print("""
  ⭐ lokal    → hech narsani o'zgartirmadi
     nonlocal → ENCLOSING o'zgardi
     global   → MODUL o'zgardi
""")


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

CHEKLOVLAR = [
    ("Nom mavjud emas",
     "def t():\n    def i():\n        nonlocal yoq\n        yoq = 1\n    i()"),
    ("Modul darajasida",
     "nonlocal x"),
    ("Global ni topish",
     "gx = 1\ndef f():\n    nonlocal gx\n    gx = 2"),
    ("Parametr bilan",
     "def t():\n    def i(x):\n        nonlocal x\n    i(1)"),
]

for nom, kod in CHEKLOVLAR:
    try:
        exec(compile(kod, "<t>", "exec"))
        natija = "✅ ishladi"
    except SyntaxError as e:
        natija = f"❌ {e.msg}"
    print(f"  {nom:<22} {natija}")

print(f"\n  ✅ To'g'ri: nom enclosing da MAVJUD bo'lishi kerak")
print(f"""
    def tashqi():
        x = 1               ← mavjud
        def ichki():
            nonlocal x      ✅
            x = 2
""")


print("\n=== 4. Eng yaqin enclosing ===")


def a():
    x = "a"

    def b():
        x = "b"                     # ⭐ eng yaqin

        def c():
            nonlocal x
            x = "c_dan"

        c()
        return x

    b_natija = b()
    return b_natija, x


b_natija, a_x = a()
print(f"  def a():  x = 'a'")
print(f"      def b():  x = 'b'")
print(f"          def c():  nonlocal x; x = 'c_dan'\n")
print(f"    b() qaytardi: {b_natija!r}   ⭐ b ning x i o'zgardi")
print(f"    a ning x i:   {a_x!r}   ✅ o'zgarmadi")


def a2():
    x = "a2"

    def b2():
        def c2():
            nonlocal x              # ⭐ b2 da x yo'q → a2 niki
            x = "c2_dan"

        c2()

    b2()
    return x


print(f"\n  def a():  x = 'a2'")
print(f"      def b():  (x yo'q)")
print(f"          def c():  nonlocal x; x = 'c2_dan'\n")
print(f"    a ning x i: {a2()!r}   ⭐ eng yaqin MAVJUD x topildi")


print("\n=== 5. Amaliy: dekoratorlarda ===")

from functools import wraps


def sanagich_dekorator(f):
    """nonlocal bilan chaqiruvlarni sanaydi."""
    chaqiruvlar = 0

    @wraps(f)
    def ichki(*args, **kwargs):
        nonlocal chaqiruvlar
        chaqiruvlar += 1
        return f(*args, **kwargs)

    ichki.chaqiruvlar = lambda: chaqiruvlar
    ichki.tozala = lambda: None     # ⚠️ nonlocal ni tashqaridan tozalab bo'lmaydi
    return ichki


def kesh_dekorator(f):
    """nonlocal bilan statistika."""
    saqlangan = {}
    topildi = topilmadi = 0

    @wraps(f)
    def ichki(*args):
        nonlocal topildi, topilmadi
        if args in saqlangan:
            topildi += 1
            return saqlangan[args]
        topilmadi += 1
        natija = saqlangan[args] = f(*args)
        return natija

    ichki.statistika = lambda: {
        "topildi": topildi,
        "topilmadi": topilmadi,
        "hajm": len(saqlangan),
    }
    ichki.tozala = saqlangan.clear
    return ichki


@sanagich_dekorator
def salom(ism):
    return f"Salom, {ism}!"


@kesh_dekorator
def fib(n):
    return n if n < 2 else fib(n - 1) + fib(n - 2)


for ism in ["Aziz", "Bobur", "Aziza"]:
    salom(ism)

print(f"  @sanagich_dekorator:")
print(f"    3 chaqiruvdan keyin: {salom.chaqiruvlar()}")

fib(20)
fib(20)
fib(10)

print(f"\n  @kesh_dekorator:")
print(f"    fib(20), fib(20), fib(10) dan keyin:")
for k, v in fib.statistika().items():
    print(f"      {k:<12} {v}")

print("""
  ⚠️ nonlocal bilan holatni tashqaridan TOZALAB bo'lmaydi
     (faqat yopilma ichidagi funksiyalar orqali).

  ⭐ Shuning uchun murakkab holat uchun SINF yaxshiroq.
""")


print("\n=== 6. nonlocal vs sinf ===")


def sanagich_nonlocal(boshlangich=0, qadam=1):
    hisob = boshlangich

    def keyingi():
        nonlocal hisob
        hisob += qadam
        return hisob

    def tozala():
        nonlocal hisob
        hisob = boshlangich

    def joriy():
        return hisob

    keyingi.tozala = tozala
    keyingi.joriy = joriy
    return keyingi


class SanagichSinf:
    def __init__(self, boshlangich=0, qadam=1):
        self._boshlangich = boshlangich
        self.qadam = qadam
        self.hisob = boshlangich

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

    def tozala(self):
        self.hisob = self._boshlangich

    def __repr__(self):
        return f"Sanagich(hisob={self.hisob}, qadam={self.qadam})"


n = sanagich_nonlocal(0, 5)
s = SanagichSinf(0, 5)

print(f"  {'Amal':<20} {'nonlocal':<14} {'sinf'}")
print("  " + "─" * 46)
for i in range(3):
    print(f"  {'chaqirish':<20} {n():<14} {s()}")
print(f"  {'joriy holat':<20} {n.joriy():<14} {s.hisob}")
n.tozala()
s.tozala()
print(f"  {'tozalashdan keyin':<20} {n.joriy():<14} {s.hisob}")

print(f"\n  Sinf afzalliklari:")
print(f"    • repr: {s!r}")
print(f"    • Atributga to'g'ridan-to'g'ri: s.hisob = 100")
s.hisob = 100
print(f"      → {s.hisob}")
print(f"    • qadam ni o'zgartirish: s.qadam = 10")
s.qadam = 10
print(f"      → {s()}")
print(f"    • Meros, xususiyatlar, metodlar")

print("""
  ⭐ nonlocal — KICHIK, mahalliy holat uchun (dekoratorlar)
     Sinf — murakkab holat, ko'p metod, meros uchun
""")

Natijaning muhim qismi:

text
=== 2. ⭐ global vs nonlocal ===
    lokal dan keyin enclosing          'ENCLOSING'
    nonlocal dan keyin enclosing       'nonlocal_dan'
    global dan keyin enclosing         'nonlocal_dan'
    Modul darajasidagi x               'global_dan'

=== 3. Cheklovlar ===
  Nom mavjud emas        ❌ no binding for nonlocal 'yoq' found
  Modul darajasida       ❌ nonlocal declaration not allowed at module level
  Global ni topish       ❌ no binding for nonlocal 'gx' found

=== 4. Eng yaqin enclosing ===
    b() qaytardi: 'c_dan'   ⭐ b ning x i o'zgardi
    a ning x i:   'a'   ✅ o'zgarmadi

Nima ko'rsatdi: 2.2, 2.3-bo'limlar.

Misol 3 — Muqobillar va ko'p oqim

python
"""Global holatdan qochish usullari."""

import threading
import time
from dataclasses import dataclass, field
from itertools import count
from functools import lru_cache

print("=== 1. Global holat muammolari ===")

jami = 0


def qosh_global(x):
    global jami
    jami += x
    return jami


print(f"  ❌ Global holat:\n")
print(f"    qosh(1) = {qosh_global(1)}")
print(f"    qosh(2) = {qosh_global(2)}")
print(f"    qosh(3) = {qosh_global(3)}")

print(f"""
  Muammolar:
    1. Test qilish qiyin — har testdan oldin `jami = 0` kerak
    2. Ikkita mustaqil hisoblagich yaratib bo'lmaydi
    3. Ko'p oqimda xavfli
    4. Funksiya imzosida bog'liqlik ko'rinmaydi
    5. Kim o'zgartirdi — kuzatish qiyin
""")

jami = 0


print("=== 2. To'rt muqobil ===\n")


# 1. Toza funksiya
def qosh_toza(jami, x):
    return jami + x


# 2. Sinf
class Hisoblagich:
    def __init__(self, boshlangich=0):
        self.jami = boshlangich

    def qosh(self, x):
        self.jami += x
        return self.jami

    def tozala(self):
        self.jami = 0
        return self


# 3. Yopilma
def hisoblagich_yarat(boshlangich=0):
    jami = boshlangich

    def qosh(x):
        nonlocal jami
        jami += x
        return jami

    qosh.joriy = lambda: jami
    return qosh


# 4. dataclass
@dataclass
class Holat:
    jami: int = 0
    tarix: list = field(default_factory=list)

    def qosh(self, x):
        self.jami += x
        self.tarix.append(x)
        return self.jami


print(f"  1. Toza funksiya:")
j = 0
for x in [1, 2, 3]:
    j = qosh_toza(j, x)
    print(f"     j = qosh(j, {x}) → {j}")

print(f"\n  2. Sinf:")
h = Hisoblagich()
for x in [1, 2, 3]:
    print(f"     h.qosh({x}) → {h.qosh(x)}")

print(f"\n  3. Yopilma:")
q = hisoblagich_yarat()
for x in [1, 2, 3]:
    print(f"     q({x}) → {q(x)}")

print(f"\n  4. dataclass:")
d = Holat()
for x in [1, 2, 3]:
    print(f"     d.qosh({x}) → {d.qosh(x)}")
print(f"     Tarix: {d.tarix}")

print(f"\n  ⭐ Ikkita mustaqil hisoblagich:")
h1, h2 = Hisoblagich(), Hisoblagich(100)
h1.qosh(5)
h2.qosh(5)
print(f"     h1.jami = {h1.jami}, h2.jami = {h2.jami}   ✅")
print(f"     Global bilan bu MUMKIN EMAS")


print("\n\n=== 3. Tayyor vositalar ===\n")

print(f"  itertools.count — hisoblagich:")
sanagich = count(1)
print(f"    next(c) = {next(sanagich)}, {next(sanagich)}, {next(sanagich)}")

sanagich2 = count(0, 10)
print(f"    count(0, 10): {[next(sanagich2) for _ in range(4)]}")

print(f"\n  functools.lru_cache — kesh:")


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


fib(30)
print(f"    fib(30) = {fib(30):,}")
print(f"    {fib.cache_info()}")
fib.cache_clear()
print(f"    cache_clear() dan keyin: {fib.cache_info()}")

print(f"\n  Funksiya atributi:")


def sanagich_atribut():
    sanagich_atribut.hisob += 1
    return sanagich_atribut.hisob


sanagich_atribut.hisob = 0
print(f"    f() = {sanagich_atribut()}, {sanagich_atribut()}, {sanagich_atribut()}")
print(f"    f.hisob = {sanagich_atribut.hisob}")


print("\n\n=== 4. ⚠️ Ko'p oqim va poyga holati ===\n")

N = 2_000
OQIMLAR = 4

sanoq_global = 0


def qosh_qulfsiz():
    global sanoq_global
    for _ in range(N):
        joriy = sanoq_global                    # 1. o'qish
        time.sleep(0)                           # ⚠️ real kodda: I/O, chaqiruv...
        sanoq_global = joriy + 1                # 2. yozish (eski qiymat asosida!)


qulf = threading.Lock()
sanoq_qulfli = 0


def qosh_qulfli():
    global sanoq_qulfli
    for _ in range(N):
        with qulf:                              # ⭐ o'qish+yozish bitta blokda
            joriy = sanoq_qulfli
            time.sleep(0)
            sanoq_qulfli = joriy + 1


def qosh_lokal(n):
    """✅ Global holatsiz."""
    return sum(1 for _ in range(n))


KUTILGAN = N * OQIMLAR

# Qulfsiz
boshlandi = time.perf_counter()
oqimlar = [threading.Thread(target=qosh_qulfsiz) for _ in range(OQIMLAR)]
for o in oqimlar: o.start()
for o in oqimlar: o.join()
vaqt_qulfsiz = time.perf_counter() - boshlandi

# Qulfli
boshlandi = time.perf_counter()
oqimlar = [threading.Thread(target=qosh_qulfli) for _ in range(OQIMLAR)]
for o in oqimlar: o.start()
for o in oqimlar: o.join()
vaqt_qulfli = time.perf_counter() - boshlandi

# Lokal
from concurrent.futures import ThreadPoolExecutor
boshlandi = time.perf_counter()
with ThreadPoolExecutor(OQIMLAR) as pool:
    natijalar = list(pool.map(qosh_lokal, [N] * OQIMLAR))
sanoq_lokal = sum(natijalar)
vaqt_lokal = time.perf_counter() - boshlandi

print(f"  {OQIMLAR} oqim × {N:,} increment (kutilgan: {KUTILGAN:,}):\n")
print(f"  {'Usul':<24} {'Natija':>12} {'To`g`rimi':>10} {'Vaqt':>10}")
print("  " + "─" * 60)
for nom, natija, vaqt in [
    ("global (qulfsiz)", sanoq_global, vaqt_qulfsiz),
    ("global + Lock", sanoq_qulfli, vaqt_qulfli),
    ("lokal (global holatsiz)", sanoq_lokal, vaqt_lokal),
]:
    belgi = "✅" if natija == KUTILGAN else "❌"
    print(f"  {nom:<24} {natija:>12,} {belgi:>9} {vaqt * 1000:>8.0f} ms")

print(f"\n  Sabab — `sanoq += 1` bir nechta instruksiya (o'qish → qo'shish → yozish):")
import dis


def increment():
    global sanoq_global
    sanoq_global += 1


ins = []
for i in dis.get_instructions(increment):
    if i.opname.startswith("RETURN"):
        break
    if i.opname != "RESUME":
        ins.append(i.opname)
if ins and ins[-1] == "LOAD_CONST":            # yashirin "return None"
    ins.pop()
print(f"    {ins}")
print(f"    ⚠️ Oqim ular ORASIDA uzilishi mumkin — GIL buni kafolatlamaydi")
print(f"    ⭐ Oddiy `+=` da CPython amalda kamdan-kam uziladi, shuning uchun")
print(f"       yuqorida o'qish va yozish orasiga sleep(0) qo'yib, oynani ochdik.")
print(f"       Real kodda bu oynani I/O yoki funksiya chaqiruvi ochadi.")


print("\n\n=== 5. threading.local ===\n")

lokal_malumot = threading.local()


def oqim_ishi(nom):
    lokal_malumot.nom = nom
    lokal_malumot.hisob = 0
    for _ in range(1000):
        lokal_malumot.hisob += 1        # ✅ har oqimga o'z nusxasi
    return f"{lokal_malumot.nom}: {lokal_malumot.hisob}"


with ThreadPoolExecutor(3) as pool:
    natijalar = list(pool.map(oqim_ishi, ["A", "B", "C"]))

print(f"  threading.local() bilan:")
for n in natijalar:
    print(f"    {n}   ✅ har oqim mustaqil")

print(f"\n  Asosiy oqimda:")
print(f"    hasattr(lokal_malumot, 'nom'): "
      f"{hasattr(lokal_malumot, 'nom')}   ⭐ o'z nusxasi")


print("\n\n=== 6. contextvars (async uchun) ===\n")

from contextvars import ContextVar, copy_context

joriy_foydalanuvchi: ContextVar[str] = ContextVar("foydalanuvchi",
                                                  default="mehmon")


def kim():
    return joriy_foydalanuvchi.get()


def ish_bajar(ism):
    token = joriy_foydalanuvchi.set(ism)
    try:
        return f"{kim()} ish bajarmoqda"
    finally:
        joriy_foydalanuvchi.reset(token)


print(f"  Boshida: {kim()!r}")
print(f"  {ish_bajar('aziz')}")
print(f"  Keyin:   {kim()!r}   ✅ tiklandi")

print(f"\n  Alohida kontekstda:")
kontekst = copy_context()
kontekst.run(joriy_foydalanuvchi.set, "bobur")
print(f"    Kontekst ichida: {kontekst.run(kim)!r}")
print(f"    Tashqarida:      {kim()!r}   ✅ ta'sir qilmadi")

print("""
  ⭐ contextvars — async va oqimlar uchun "global" holat:
     • Har kontekst (task/thread) o'z qiymatini ko'radi
     • Web freymvorklarida so'rov konteksti uchun ishlatiladi
""")


print("\n=== 7. Qaror jadvali ===\n")

print("""
  ┌────────────────────────────┬──────────────────────────────────┐
  │ Ehtiyoj                    │ Yechim                           │
  ├────────────────────────────┼──────────────────────────────────┤
  │ Hisoblash natijasi         │ return ⭐ (toza funksiya)         │
  │ Murakkab holat + metodlar  │ Sinf ⭐                           │
  │ Kichik holat (dekorator)   │ nonlocal                         │
  │ Hisoblagich                │ itertools.count                  │
  │ Kesh                       │ functools.lru_cache              │
  │ Modul singleton'i          │ global (kirish funksiyasi bilan) │
  │ Oqimga bog'liq holat       │ threading.local                  │
  │ Async kontekst             │ contextvars                      │
  │ Ko'p oqim + umumiy holat   │ Lock, Queue, yoki holatsiz ⭐     │
  │ Konfiguratsiya             │ frozen dataclass / modul         │
  └────────────────────────────┴──────────────────────────────────┘
""")

Natijaning muhim qismi:

text
=== 4. ⚠️ Ko'p oqim va poyga holati ===

  4 oqim × 2,000 increment (kutilgan: 8,000):

  Usul                           Natija  To`g`rimi       Vaqt
  ────────────────────────────────────────────────────────────
  global (qulfsiz)                2,516         ❌        8 ms
  global + Lock                   8,000         ✅       33 ms
  lokal (global holatsiz)         8,000         ✅        1 ms

  Sabab — `sanoq += 1` bir nechta instruksiya (o'qish → qo'shish → yozish):
    ['LOAD_GLOBAL', 'LOAD_SMALL_INT', 'BINARY_OP', 'STORE_GLOBAL']
    ⚠️ Oqim ular ORASIDA uzilishi mumkin — GIL buni kafolatlamaydi

=== 5. threading.local ===
    A: 1000   ✅ har oqim mustaqil
    B: 1000   ✅ har oqim mustaqil
    C: 1000   ✅ har oqim mustaqil

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

Misol 4 — Amaliy: modul holati va refaktoring

python
"""Global holatni to'g'ri boshqarish va undan qochish."""

import sys
import threading
import time
from dataclasses import dataclass, field
from typing import Any

print("=== 1. Modul singleton naqshi ===\n")

# ─── "Modul" simulyatsiyasi ───

_ULANISH: Any = None
_ULANISHLAR_SONI = 0
_KESH: dict[str, Any] = {}
_QULF = threading.Lock()


def _ulan():
    """Qimmat resurs yaratish."""
    global _ULANISHLAR_SONI
    _ULANISHLAR_SONI += 1
    time.sleep(0.001)
    return f"<ulanish#{_ULANISHLAR_SONI}>"


def ulanish_ol():
    """⭐ Singleton — bir marta yaratiladi."""
    global _ULANISH
    if _ULANISH is None:
        with _QULF:
            if _ULANISH is None:            # ⭐ ikki marta tekshirish
                _ULANISH = _ulan()
    return _ULANISH


def ulanishni_yop():
    """Resursni bo'shatish."""
    global _ULANISH
    if _ULANISH is not None:
        _ULANISH = None


def keshdan_ol(kalit, yaratuvchi):
    """Kesh — global KERAK EMAS (obyektni o'zgartirish)."""
    if kalit not in _KESH:
        _KESH[kalit] = yaratuvchi()
    return _KESH[kalit]


def keshni_tozala():
    """✅ global kerak emas — clear() obyektni o'zgartiradi."""
    _KESH.clear()


print(f"  Singleton ulanish:")
for i in range(3):
    print(f"    ulanish_ol() → {ulanish_ol()}")
print(f"    Yaratilgan ulanishlar: {_ULANISHLAR_SONI}   ⭐ bitta")

ulanishni_yop()
print(f"\n  Yopilgandan keyin:")
print(f"    ulanish_ol() → {ulanish_ol()}   (yangi yaratildi)")

print(f"\n  Kesh:")
for kalit in ["a", "b", "a"]:
    qiymat = keshdan_ol(kalit, lambda: f"qiymat_{kalit}_{time.time():.6f}")
    print(f"    keshdan_ol({kalit!r}) → {qiymat}")
print(f"    Kesh hajmi: {len(_KESH)}")
keshni_tozala()
print(f"    Tozalangandan keyin: {len(_KESH)}")

print("""
  ⭐ Modul holati uchun qoidalar:
     1. Nom `_` bilan boshlansin (xususiy)
     2. Kirish faqat funksiyalar orqali
     3. Ko'p oqim uchun Lock
     4. Tozalash funksiyasi bo'lsin (test uchun)
""")


print("\n=== 2. ⚠️ from import muammosi ===\n")

# Ikki modulni simulyatsiya qilish
MODUL_A = """
x = 1

def ozgartir():
    global x
    x = 2

def ol():
    return x
"""

import types

a_modul = types.ModuleType("a_modul")
exec(MODUL_A, a_modul.__dict__)
sys.modules["a_modul"] = a_modul

# b.py: from a import x
from a_modul import x as import_qilingan_x, ozgartir, ol
import a_modul

print(f"  # a.py:  x = 1")
print(f"  # b.py:  from a import x, ozgartir")
print(f"           import a\n")

print(f"    Boshida:")
print(f"      import_qilingan_x = {import_qilingan_x}")
print(f"      a_modul.x         = {a_modul.x}")

ozgartir()

print(f"\n    ozgartir() dan keyin:")
print(f"      import_qilingan_x = {import_qilingan_x}   ⚠️ o'zgarmadi!")
print(f"      a_modul.x         = {a_modul.x}   ✅")
print(f"      ol()              = {ol()}   ✅ (modul ichidan)")

print("""
  ⭐ `from a import x` — x ning JORIY qiymatiga yangi nom bog'laydi.
     Keyingi o'zgarishlar ko'rinmaydi.

  ✅ O'zgaruvchan global holat uchun:
     import modul
     modul.x
""")

del sys.modules["a_modul"]


print("\n=== 3. Refaktoring: global → sinf ===\n")

print(f"  ❌ ASL (global holat bilan):\n")
print("""    _sozlamalar = {}
    _tarix = []
    _joriy_foydalanuvchi = None

    def kirish(ism):
        global _joriy_foydalanuvchi
        _joriy_foydalanuvchi = ism
        _tarix.append(("kirish", ism))

    def sozla(kalit, qiymat):
        if _joriy_foydalanuvchi is None:
            raise RuntimeError("avval kiring")
        _sozlamalar[kalit] = qiymat
        _tarix.append(("sozla", kalit, qiymat))

    def chiqish():
        global _joriy_foydalanuvchi
        _tarix.append(("chiqish", _joriy_foydalanuvchi))
        _joriy_foydalanuvchi = None
""")


@dataclass
class Sessiya:
    """✅ REFAKTORING: holat obyektda."""
    sozlamalar: dict[str, Any] = field(default_factory=dict)
    tarix: list[tuple] = field(default_factory=list)
    joriy_foydalanuvchi: str | None = None

    def kirish(self, ism: str) -> "Sessiya":
        self.joriy_foydalanuvchi = ism
        self.tarix.append(("kirish", ism))
        return self

    def sozla(self, kalit: str, qiymat: Any) -> "Sessiya":
        if self.joriy_foydalanuvchi is None:
            raise RuntimeError("avval kiring")
        self.sozlamalar[kalit] = qiymat
        self.tarix.append(("sozla", kalit, qiymat))
        return self

    def chiqish(self) -> "Sessiya":
        self.tarix.append(("chiqish", self.joriy_foydalanuvchi))
        self.joriy_foydalanuvchi = None
        return self

    def hisobot(self) -> str:
        qatorlar = [f"Foydalanuvchi: {self.joriy_foydalanuvchi or '(kirmagan)'}"]
        qatorlar.append(f"Sozlamalar: {self.sozlamalar}")
        qatorlar.append(f"Tarix ({len(self.tarix)} amal):")
        for amal in self.tarix:
            qatorlar.append(f"  • {' '.join(map(str, amal))}")
        return "\n".join(qatorlar)


print(f"  ✅ Sinf bilan:\n")

s = Sessiya()
s.kirish("aziz").sozla("til", "uz").sozla("tema", "qora").chiqish()

for q in s.hisobot().splitlines():
    print(f"    {q}")

print(f"\n  ⭐ Afzalliklar:\n")

s1 = Sessiya().kirish("aziz").sozla("til", "uz")
s2 = Sessiya().kirish("bobur").sozla("til", "en")

print(f"    1. Ikki mustaqil sessiya:")
print(f"       s1: {s1.joriy_foydalanuvchi}, til={s1.sozlamalar['til']}")
print(f"       s2: {s2.joriy_foydalanuvchi}, til={s2.sozlamalar['til']}")

print(f"\n    2. Test oson — har testda yangi obyekt")
print(f"    3. Zanjirlash: s.kirish().sozla().chiqish()")
print(f"    4. Ko'p oqim: har oqimga o'z Sessiya()")
print(f"    5. Holat repr da ko'rinadi")

try:
    Sessiya().sozla("x", 1)
except RuntimeError as e:
    print(f"\n    6. Validatsiya ishlaydi: ❌ {e}")


print("\n\n=== 4. Qachon global maqbul ===\n")

print("""  ✅ Maqbul holatlar:

    1. Modul konstantalari (o'zgarmas)
       MAKSIMAL_URINISH = 3
       RUXSAT_ETILGAN = frozenset({...})

    2. Singleton resurs (kirish funksiyasi bilan)
       def ulanish_ol():
           global _ULANISH
           if _ULANISH is None: ...

    3. Kesh (ichki, xususiy)
       _KESH = {}                  # global kerak emas — o'zgartirish

    4. Ro'yxatga olish (registry)
       _HANDLERLAR = {}
       def royxatga_ol(nom): ...

    5. Kichik skript / prototip

  ❌ Nomaqbul:

    1. Ilova holati (foydalanuvchi, sessiya, ma'lumot)
    2. Hisoblash natijalarini yig'ish
    3. Funksiyalar orasida ma'lumot uzatish
    4. Ko'p oqimli kod (Lock siz)
    5. Kutubxona ichida (foydalanuvchi kutmaydi)
""")


print("=== 5. Global holat testlari ===\n")

_TEST_HOLAT = {"sanoq": 0}


def global_bilan_funksiya():
    _TEST_HOLAT["sanoq"] += 1
    return _TEST_HOLAT["sanoq"]


def toza_funksiya(sanoq):
    return sanoq + 1


print(f"  Global holat bilan test:")
print(f"""
    def test_1():
        _TEST_HOLAT["sanoq"] = 0        # ⚠️ tiklash KERAK
        assert global_bilan_funksiya() == 1

    def test_2():
        _TEST_HOLAT["sanoq"] = 0        # ⚠️ yana
        assert global_bilan_funksiya() == 1
""")

print(f"  Test tartibiga bog'liqlik:")
_TEST_HOLAT["sanoq"] = 0
n1 = global_bilan_funksiya()
n2 = global_bilan_funksiya()
print(f"    Ketma-ket: {n1}, {n2}   ⚠️ ikkinchi test 1 kutgan bo'lardi")

print(f"\n  Toza funksiya bilan:")
print(f"""
    def test_1():
        assert toza_funksiya(0) == 1    ✅ tiklash kerak emas

    def test_2():
        assert toza_funksiya(0) == 1    ✅ mustaqil
""")
print(f"    toza_funksiya(0) = {toza_funksiya(0)}, "
      f"{toza_funksiya(0)}   ✅ har doim bir xil")

print("""
  ⭐ pytest fixture bilan global holatni tiklash:

     @pytest.fixture(autouse=True)
     def holatni_tikla():
         _TEST_HOLAT["sanoq"] = 0
         yield
         _TEST_HOLAT.clear()

     Lekin eng yaxshisi — global holatdan voz kechish.
""")


print("=== 6. Yakuniy tavsiyalar ===\n")

print("""
  1️⃣ Birinchi savol: "Global holat kerakmi?"
     Ko'p holatda javob — YO'Q.

  2️⃣ Agar kerak bo'lsa, tartib bo'yicha:
     • return (toza funksiya)         ⭐
     • Sinf / dataclass
     • Yopilma + nonlocal
     • Modul + kirish funksiyalari
     • global (oxirgi chora)

  3️⃣ global ishlatsangiz:
     • Nom `_` bilan (xususiy)
     • Faqat funksiyalar orqali kirish
     • Ko'p oqimda Lock
     • Tozalash funksiyasi (test uchun)
     • Hujjatda yozing

  4️⃣ nonlocal ishlatsangiz:
     • Faqat kichik, mahalliy holat
     • Dekoratorlar uchun ideal
     • Murakkablashsa — sinfga o'ting
""")

Natijaning muhim qismi:

text
=== 1. Modul singleton naqshi ===

  Singleton ulanish:
    ulanish_ol() → <ulanish#1>
    ulanish_ol() → <ulanish#1>
    ulanish_ol() → <ulanish#1>
    Yaratilgan ulanishlar: 1   ⭐ bitta

=== 2. ⚠️ from import muammosi ===
    ozgartir() dan keyin:
      import_qilingan_x = 1   ⚠️ o'zgarmadi!
      a_modul.x         = 2   ✅

=== 3. Refaktoring: global → sinf ===
    1. Ikki mustaqil sessiya:
       s1: aziz, til=uz
       s2: bobur, til=en

Nima ko'rsatdi: 2.3, 2.4, 2.7-bo'limlar.


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

Noto'g'ri fikr To'g'risi
"O'qish uchun global kerak" Faqat yozish uchun
"d[k] = v uchun global kerak" Obyektni o'zgartirish — kerak emas
"nonlocal global ni topadi" SyntaxError
"nonlocal yangi nom yaratadi" Nom mavjud bo'lishi shart
"global faqat funksiya boshida" Istalgan joyda, lekin ishlatishdan oldin
"GIL global holatni himoya qiladi" += — bir nechta instruksiya; GIL ular orasida uzilmaslikni kafolatlamaydi
"from a import x havola beradi" Nusxa — keyingi o'zgarish ko'rinmaydi
"global shart ichida ta'sir qilmaydi" Kompilyator butun tanani ko'radi
"Yopilma holati — global kabi yomon" Mahalliy va nazorat ostida

6. Keng tarqalgan xatolar va yechimlari

1. global unutish

python
sanoq = 0
def f():
    sanoq += 1                      # ❌ UnboundLocalError
    global sanoq                    # ✅ (birinchi qatorga)

2. Ortiqcha global

python
def f():
    global x
    return x                        # ⚠️ o'qish uchun kerak emas

3. nonlocal mavjud bo'lmagan nom uchun

python
def t():
    def i():
        nonlocal x                  # ❌ SyntaxError
        x = 1

4. nonlocal global uchun

python
x = 1
def f():
    nonlocal x                      # ❌ SyntaxError
    global x                        # ✅

5. from import bilan o'zgaruvchan global

python
from modul import sanoq             # ⚠️ nusxa
import modul; modul.sanoq           # ✅

6. Ko'p oqimda qulfsiz

python
def f():
    global sanoq
    sanoq += 1                      # ⚠️ race condition

with qulf:                          # ✅
    sanoq += 1

7. Global holat testda

python
def test():
    assert f() == 1                 # ⚠️ oldingi testga bog'liq

@pytest.fixture(autouse=True)       # ✅
def tikla(): _HOLAT.clear()

8. Kutubxonada global holat

python
# kutubxona.py
_natija = None                      # ⚠️ foydalanuvchi kutmaydi

class Ishlov:                       # ✅
    def __init__(self): self.natija = None

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 7.8-dars (o'tilgan): LEGB, UnboundLocalError
  • 7.2-dars (o'tilgan): o'zgartirish vs qayta bog'lash
  • 8-qism: sinflar — holatning to'g'ri joyi
  • 10-qism: dekoratorlar (nonlocal bilan)
  • 19-qism: modullar, import semantikasi
  • 14-qism: ko'p oqim, threading, asyncio, contextvars
  • 17-qism: testlash, fixture, izolyatsiya

8. Eng yaxshi amaliyotlar

  1. Global holatdan qoching. Qaytarish, sinf yoki yopilma ishlating.

  2. global — faqat yozish uchun. O'qish uchun kerak emas.

  3. Obyektni o'zgartirish uchun global kerak emas. _KESH[k] = v .

  4. Global nomlar _ bilan. Kirish faqat funksiyalar orqali.

  5. nonlocal — kichik, mahalliy holat uchun. Dekoratorlarda ideal.

  6. O'zgaruvchan global uchun import modul. from modul import x — nusxa.

  7. Ko'p oqimda Lock yoki holatsiz dizayn. GIL yetarli emas.

  8. Global holat bo'lsa — tozalash funksiyasi yozing. Testlar uchun.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
x = 1
d = {}

1.  def f(): return x
    print(f())
2.  def f(): x = 2
    f(); print(x)
3.  def f():
        global x
        x = 2
    f(); print(x)
4.  def f(): d["a"] = 1
    f(); print(d)
5.  def f(): d = {}
    f(); print(d)
6.  def f():
        print(x); x = 2
    f()
7.  def t():
        y = 1
        def i(): nonlocal y; y = 2
        i(); return y
    print(t())
8.  def t():
        def i(): nonlocal z; z = 1
        i()
9.  def f():
        global gz
        gz = 1
    f(); print(gz)
10. def f():
        if False: global w
        w = 1
    f(); print(w)
11. def t():
        a = 1
        def i(): a = 2
        i(); return a
    print(t())
12. def f(p):
        global p
Javoblar
  1. 1
  2. 1 — lokal
  3. 2
  4. {'a': 1} — obyektni o'zgartirish
  5. {'a': 1} — qayta bog'lash lokal
  6. UnboundLocalError
  7. 2
  8. SyntaxError — z mavjud emas
  9. 1 — yangi global yaratildi
  10. 1 — shart ahamiyatsiz
  11. 1 — ichki a alohida
  12. SyntaxError — parametr va global

Vazifa 2: Xatolarni tuzating

python
1.  sanoq = 0
    def qosh(): sanoq += 1
2.  def t():
        h = 0
        def i(): h += 1
        return i
3.  x = 1
    def f():
        nonlocal x
        x = 2
4.  from modul import sanoq
    modul.oshir(); print(sanoq)
5.  def f():
        global sanoq
        sanoq += 1      # ko'p oqimda
6.  _KESH = {}
    def f():
        global _KESH
        _KESH["a"] = 1
7.  def f():
        x = 1
        global x
8.  jami = 0
    def qosh(x):
        global jami
        jami += x
    # test yozish kerak
Javoblar
python
1.  def qosh(): global sanoq; sanoq += 1
2.  def i(): nonlocal h; h += 1
3.  global x  (nonlocal emas)
4.  import modul; modul.oshir(); print(modul.sanoq)
5.  with qulf: sanoq += 1   (yoki holatsiz dizayn)
6.  global kerak emas — obyektni o'zgartirish
7.  global x ni birinchi qatorga
8.  Sinfga o'tkazing yoki def qosh(jami, x): return jami + x

Vazifa 3: Refaktoring

Global holatli kodni uch xil qayta yozing:

python
_foydalanuvchi = None
_sozlamalar = {}
_tarix = []

def kirish(ism): ...
def sozla(k, v): ...
def chiqish(): ...
def tarix_ol(): ...
  1. Toza funksiyalar (holat argument sifatida)
  2. Sinf
  3. Yopilma + nonlocal
  4. Har biri uchun testlar
  5. Ikki mustaqil sessiya yaratib ko'ring
  6. Qaysi biri qachon yaxshiroq — tahlil

Vazifa 4: Holat vositalari

Yozing:

  1. Sanagich — __call__, tozala, qadam, kontekst menejeri
  2. Kesh — maxsize, ttl, statistika
  3. Registr — royxatga_ol dekoratori, chaqir, royxat
  4. Sessiya — kirish/chiqish, tarix, with bilan
  5. Har biri global holatsiz bo'lsin
  6. Ko'p oqimga xavfsiz variantlarini ham

Vazifa 5: Poyga holati tadqiqoti

Dastur yozing:

  1. += ning atomik emasligini ko'rsating
  2. Turli oqim soni va iteratsiyada xatoni o'lchang
  3. Yechimlar: Lock, RLock, atomic (itertools.count)
  4. Har birining tezligini solishtiring
  5. threading.local va contextvars bilan
  6. GIL nimani himoya qiladi, nimani yo'q

Vazifa 6: Modul holati auditi

Vosita yozing:

  1. Modul darajasidagi o'zgaruvchan obyektlarni topsin (ast)
  2. Qaysi funksiyalar global ishlatishini
  3. from x import y bilan import qilingan o'zgaruvchan nomlarni
  4. Ko'p oqim xavfini baholasin
  5. Refaktoring taklifini bersin
  6. Hisobot chiqarsin

Vazifa 7: O'ylash

Nega Python global va nonlocal ni e'lon qildi, boshqa tillar kabi $global yoki outer.x sintaksisi o'rniga?

Javob

Chunki e'lon kompilyatsiya paytida ishlaydi va o'qilishi aniq.

Muqobil dizaynlar:

1. Prefiks (PHP, Perl):

php
$GLOBALS['x'] = 1;          // PHP
perl
our $x = 1;                 # Perl

Muammo: har murojaatda prefiks yozish kerak. Kod shovqinli.

2. Qamrov ko'rsatkichi (hipotetik):

python
outer.x = 1                 # enclosing
module.x = 1                # global

Muammolar:

  • outer va module — yangi kalit so'zlar
  • Ular obyektmi yoki sintaksismi?
  • Ichma-ich uch daraja bo'lsa: outer.outer.x?
  • Mavjud kod bilan to'qnashuv (outer — oddiy nom bo'lishi mumkin)

3. Aniq e'lon (JavaScript var/let):

javascript
function f() {
    let x = 1;              // lokal
    y = 2;                  // ⚠️ global (aniq e'lon YO'Q)
}

Muammo: teskari — e'lonsiz nom global bo'lib qoladi. Bu — JavaScript ning eng ko'p tanqid qilingan xatolaridan biri ("use strict" bilan tuzatilgan).

4. Python tanlagan: e'lon

python
def f():
    global x
    x = 1

Afzalliklari:

a) Kompilyatsiya paytida ishlaydi.

Kompilyator butun funksiya tanasini skanerlaydi. global x ko'rsa, barcha x uchun STORE_GLOBAL/LOAD_GLOBAL beradi. Ish vaqtida qo'shimcha tekshiruv yo'q.

b) Sukut — lokal (xavfsiz).

python
def f():
    x = 1                   # lokal — tasodifan global o'zgartirilmaydi

Bu — JavaScript var dan ancha xavfsizroq. Tasodifan global yaratish mumkin emas.

c) Niyat funksiya boshida ko'rinadi.

python
def f():
    global sanoq, kesh      # ⭐ darhol ko'rinadi
    ...

O'quvchi funksiyaning global holatga tegishini birinchi qatorlarda biladi.

d) Kam yoziladi.

E'lon bir marta, ishlatish ko'p marta:

python
def f():
    global x
    x += 1
    x *= 2
    return x                # har safar prefiks kerak emas

e) Grep qilish oson.

bash
grep -n "global " *.py      # barcha global yozuvlarni topadi

f) Linterlar tekshiradi.

pylint W0603: global-statement
ruff PLW0603

Global ishlatish darhol ko'rinadi va ogohlantiriladi.

Kamchiliklar:

a) E'lon va ishlatish ajralgan:

python
def f():
    global x
    # ... 50 qator ...
    x = 1                   # ⚠️ global ekanini eslash kerak

Lekin bu — uzun funksiya muammosi, global ning emas.

b) nonlocal cheklovi:

python
def t():
    def i():
        nonlocal x          # ❌ x mavjud emas
        x = 1

Yangi nom yaratib bo'lmaydi. Bu — ataylab: nonlocal "mavjud nomni o'zgartirish" degani.

c) Ikkita kalit so'z:

global va nonlocal — o'rganish kerak. Lekin ular kam ishlatiladi.

Tarixiy nozik:

nonlocal faqat Python 3.0 da qo'shildi (PEP 3104, 2006). Undan oldin yopilma o'zgaruvchisini o'zgartirish uchun hiyla kerak edi:

python
def sanagich():
    hisob = [0]             # ⚠️ ro'yxat hiylasi
    def ichki():
        hisob[0] += 1
        return hisob[0]
    return ichki

PEP 3104 muhokamasida muqobil sintaksislar ko'rilgan:

python
.x = 1                      # nuqta prefiksi
:=                          # o'shanda walrus uchun emas edi
outer x                     # kalit so'z
global x in f               # murakkab

nonlocal tanlandi — chunki u global bilan simmetrik va o'qilishi aniq.

Boshqa tillar:

Til Enclosing o'zgartirish
Python nonlocal x
JavaScript Avtomatik (closure)
Ruby Avtomatik (block)
Swift Avtomatik (closure)
C++ [&x] (lambda capture)
Rust move yoki &mut

Qiziq: ko'p til yopilmani avtomatik o'zgartiruvchan qiladi. Python esa aniq e'lon talab qiladi.

Sabab — Pythonda o'zlashtirish nomni lokal qiladi. Bu qoida barcha qamrov uchun bir xil. nonlocal — bu qoidadan aniq istisno.

Xulosa: e'lon yondashuvi Pythonning ikki tamoyiliga mos:

  1. "Aniq yashirinlikdan yaxshi" — global o'zgartirish ko'rinadi
  2. "Sukut xavfsiz bo'lsin" — tasodifan global yaratib bo'lmaydi

Va u kompilyatsiya paytida ishlaydi — tezlik uchun ham foydali.

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


Xulosa

Bu darsda global va nonlocal ni o'rgandik.

Eng muhim uch fikr:

  1. global — modul qamroviga, nonlocal — enclosing qamroviga yozish. Ikkalasi ham faqat yozish uchun kerak — o'qish uchun emas. Va nonlocal da nom mavjud bo'lishi shart, global esa yangisini yaratadi.

  2. Obyektni o'zgartirish uchun global kerak emas. _KESH["a"] = 1 — , _KESH = {} — global kerak. Bu 7.2-darsdagi "o'zgartirish vs qayta bog'lash" farqi.

  3. Global holat — oxirgi chora. Test qilish qiyin, ko'p oqimda xavfli, bog'liqlik yashirin. Muqobillar tartibi: qaytarish (toza funksiya) → sinf → yopilma + nonlocal → modul singleton'i → global.

Keyingi darsda lambda ni ko'ramiz: anonim funksiyalar, ularning cheklovlari va qachon oddiy def yaxshiroq.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
7.9-dars: global va nonlocal — IlmHamroh