aidoc.uz

1-qism · Poydevor·02-bob

O'zgaruvchilar, obyektlar va asosiy tiplar

Python'da 'hamma narsa obyekt' degani nima, o'zgaruvchi aslida nom ekanligi, mutable va immutable tiplar, int, float, bool, None va sonlar bilan ishlashdagi tuzoqlar.

Bu bob — butun kursning eng muhim boblaridan biri. Sababi: Python'da o'zgaruvchilar boshqa ko'p tillardagidek ishlamaydi va buni tushunmaslik eng ko'p uchraydigan, eng qiyin topiladigan xatolarga olib keladi. Masalan, ML kodida "nega mening asl ma'lumotlarim o'zgarib qoldi?" degan savolning 90% javobi shu bobda.

▶ Video dars· Sreekanth· 15:16· ingliz tilida
How variables work in Python | Explained with Animations [See description/first comment]

O'zgaruvchilar "quti" emas, obyektga yopishtirilgan "yorliq" ekanini animatsiyalar bilan ko'rsatadi — bu bobning eng muhim g'oyasi (2-bo'lim).

YouTube'da ochish ↗

1. Hamma narsa — obyekt#

Python'da har bir qiymat — obyekt: son, matn, ro'yxat, funksiya, klass, hatto modul ham. Har bir obyektning uchta xususiyati bor:

XususiyatNimaQanday ko'rish mumkinO'zgaradimi?
Identifikator (identity)Obyektning xotiradagi "manzili"id(x)Hech qachon
Tip (type)Obyekt qanday turdagi va nima qila oladitype(x)Hech qachon
Qiymat (value)Obyekt ichidagi ma'lumotprint(x)Faqat mutable obyektlarda
>>> x = 42
>>> id(x)
4354771528          # sizda boshqa son chiqadi
>>> type(x)
<class 'int'>
>>> type(print)
<class 'builtin_function_or_method'>
>>> type(int)
<class 'type'>      # klass ham obyekt — uning tipi 'type'

2. O'zgaruvchi — bu quti emas, yorliq#

Ko'p darsliklar o'zgaruvchini "qiymat solinadigan quti" deb tushuntiradi. C tilida bu to'g'ri. Python'da esa bu noto'g'ri va xavfli tasavvur.

Python'da o'zgaruvchi — bu obyektga yopishtirilgan nom (yorliq, havola). x = 42 degani: "xotirada 42 obyektini yarat (yoki top) va unga x degan yorliqni yopishtir".

a
nom (yorliq)
[1, 2, 3]
list obyekti, id=4401…
b
nom (yorliq)
Ikkita nom bitta obyektga ishora qilmoqda

Buning oqibati — mana bu klassik "tuzoq":

a = [1, 2, 3]
b = a            # NUSXA EMAS! b — o'sha ro'yxatga ikkinchi yorliq
b.append(4)
 
print(a)         # [1, 2, 3, 4]  ← a ham "o'zgardi"
print(a is b)    # True — bitta obyekt

b = a hech qachon nusxa yaratmaydi. U shunchaki a ishora qilayotgan obyektga yana bitta nom beradi. Ro'yxat bitta — nomlar ikkita.

Endi solishtiring:

a = [1, 2, 3]
b = a
b = [9, 9]       # b'ni BOSHQA obyektga ko'chirdik — a'ga ta'sir yo'q
print(a)         # [1, 2, 3]

Farq nimada? b.append(4) — obyektni o'zgartirdi (mutatsiya). b = [9, 9] — nomni boshqa obyektga ko'chirdi (qayta bog'lash, rebinding). Bu ikki amalni farqlash — Python'ni tushunishning kaliti.

Qayta bog'lash (=)
  • Nom boshqa obyektga ishora qila boshlaydi
  • Eski obyekt o'zgarmaydi
  • Boshqa nomlarga ta'sir qilmaydi
  • Misol: x = x + 1, s = s.upper()
Mutatsiya (metod / indeks)
  • Obyektning o'zi o'zgaradi
  • Shu obyektga ishora qiluvchi BARCHA nomlar o'zgarishni ko'radi
  • Faqat mutable obyektlarda mumkin
  • Misol: lst.append(1), d['k'] = 2, lst[0] = 5

Savol bering: 'men nomni ko'chiryapmanmi yoki obyektni o'zgartiryapmanmi?'

is va == farqi#

  • a == b — qiymatlar tengmi? (__eq__ metodini chaqiradi)
  • a is b — bitta obyektmi? (id(a) == id(b) bilan bir xil)
x = [1, 2, 3]
y = [1, 2, 3]
print(x == y)    # True  — qiymatlari bir xil
print(x is y)    # False — ikki xil obyekt
 
z = x
print(x is z)    # True

Qoida: isni faqat None, True, False bilan solishtirishda ishlating (if x is None:). Qolgan hamma joyda ==.

3. Mutable va immutable#

Bu Python'ning eng muhim tushunchalaridan biri:

Immutable (o'zgarmas)Mutable (o'zgaruvchan)
int, float, complex, boollist
str, bytesdict
tuple, frozensetset
Nonebytearray
O'zingiz yozgan klasslar (odatda)

Immutable obyektni o'zgartirib bo'lmaydi — "o'zgartirish"ga o'xshagan har qanday amal aslida yangi obyekt yaratadi:

s = "salom"
print(id(s))
s = s.upper()     # yangi "SALOM" obyekti yaratildi, s unga ko'chirildi
print(id(s))      # boshqa id
 
n = 10
print(id(n))
n += 1            # 11 — yangi obyekt
print(id(n))      # boshqa id
lst = [1, 2]
print(id(lst))
lst += [3]        # list uchun += — joyida o'zgartirish (extend)
print(id(lst))    # o'sha id!

Diqqat qiling: += int uchun yangi obyekt yaratadi, list uchun esa mavjudini o'zgartiradi. Bu __iadd__ metodiga bog'liq — 13-bobda ko'ramiz.

Nega bu ML kodida muhim?#

def normalize(data):
    """Har bir qiymatni maksimumga bo'ladi."""
    m = max(data)
    for i in range(len(data)):
        data[i] = data[i] / m      # ← asl ro'yxatni o'zgartiradi!
    return data
 
raw = [10, 20, 40]
norm = normalize(raw)
print(norm)   # [0.25, 0.5, 1.0]
print(raw)    # [0.25, 0.5, 1.0]  ← asl ma'lumot yo'qoldi!

Funksiyaga ro'yxat berilganda nusxa emas, o'sha obyekt beriladi. Funksiya uni o'zgartirsa, chaqiruvchi ham o'zgarishni ko'radi. To'g'ri yo'l — yangi ro'yxat qaytarish:

def normalize(data):
    m = max(data)
    return [x / m for x in data]   # yangi ro'yxat — asl ma'lumot daxlsiz

4. Dinamik tiplash#

Python'da o'zgaruvchining tipi yo'q — obyektning tipi bor. Bitta nom turli vaqtda turli tipdagi obyektlarga ishora qilishi mumkin:

x = 42          # x → int
x = "qirq ikki" # x → str
x = [4, 2]      # x → list

Bu qulay, lekin xavfli: tip xatosi faqat kod ishlaganda chiqadi.

def narx_hisobla(soni, narxi):
    return soni * narxi
 
print(narx_hisobla(3, 1500))      # 4500
print(narx_hisobla("3", 1500))    # "333...3" — 1500 marta takrorlangan matn! Xato emas!

Ikkinchi chaqiruv xato bermaydi — str * int Python'da ruxsat etilgan amal (satrni takrorlash). Bunday xatolar jim o'tib ketadi. Shu sababli professional kodda type hint va mypy ishlatiladi (15-bob):

def narx_hisobla(soni: int, narxi: int) -> int:
    return soni * narxi

Python baribir tekshirmaydi, lekin muharrir va mypy narx_hisobla("3", 1500) ni darhol qizil chiziq bilan belgilaydi.

Kuchli tiplash. Python dinamik, lekin kuchli tiplangan: turli tiplarni o'zi aylantirib yubormaydi. JavaScript'da "3" + 1 → "31"; Python'da → TypeError. Bu yaxshi narsa.

5. Sonlar#

int — chegarasiz butun sonlar#

Python'da butun sonlarning chegarasi yo'q (faqat xotira):

>>> 2 ** 64
18446744073709551616
>>> 2 ** 1000           # muammosiz ishlaydi
10715086071862673209484250490600018105614048117055336074437503883703510511249...

Katta sonlarni o'qish uchun _ ishlatish mumkin: 1_000_000. Boshqa sanoq tizimlari: 0b1010 (ikkilik, 10), 0xff (o'n oltilik, 255), 0o17 (sakkizlik, 15).

Arifmetik operatorlar#

OperatorMa'noMisolNatija
+ - *Qo'shish, ayirish, ko'paytirish7 * 214
/Bo'lish — har doim float7 / 2, 6 / 23.5, 3.0
//Butun bo'lish (pastga yaxlitlash)7 // 2, -7 // 23, -4
%Qoldiq7 % 2, -7 % 21, 1
**Daraja2 ** 101024
divmod(a, b)(a // b, a % b)divmod(17, 5)(3, 2)

E'tibor bering: -7 // 2 → -4, -3 emas. // har doim pastga (manfiy cheksizlik tomon) yaxlitlaydi. int(-3.9) esa nolga tomon kesadi → -3. Bu farq indekslar va batch'larni hisoblashda xatoga olib kelishi mumkin.

Amaliy misol — ma'lumotlarni batch'larga bo'lish:

n_samples = 1050
batch_size = 64
 
full_batches = n_samples // batch_size          # 16
remainder = n_samples % batch_size              # 26
n_batches = -(-n_samples // batch_size)         # 17 — yuqoriga yaxlitlash hiylasi
# yoki: import math; math.ceil(n_samples / batch_size)

float — va 0.1 + 0.2 muammosi#

>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False

Bu Python xatosi emas — bu IEEE 754 standartidagi barcha tillarda shunday (JavaScript, Java, C). Sababi: 0.1 ni ikkilik sanoq tizimida aniq yozib bo'lmaydi (xuddi 1/3 ni o'nlik kasrda aniq yozib bo'lmagandek: 0.3333...). Kompyuter eng yaqin qiymatni saqlaydi va kichik xatolar to'planadi.

Uchta qoida:

import math
 
# 1. float'larni hech qachon == bilan solishtirmang
math.isclose(0.1 + 0.2, 0.3)          # True
 
# 2. Pul uchun Decimal ishlating
from decimal import Decimal
Decimal("0.1") + Decimal("0.2")       # Decimal('0.3') — aniq
# Diqqat: Decimal(0.1) emas, Decimal("0.1") — satrdan!
 
# 3. Maxsus qiymatlarni biling
float("inf"), float("-inf")           # cheksizlik
float("nan")                          # "Not a Number"
float("nan") == float("nan")          # False! NaN hech narsaga teng emas, o'ziga ham
math.isnan(float("nan"))              # True — NaN'ni shunday tekshiring

Yaxlitlash: round(2.5) → 2, round(3.5) → 4. Bu xato emas — Python "bankir yaxlitlashi"ni (banker's rounding) ishlatadi: aniq o'rtadagi son juft tomonga yaxlitlanadi. Bu katta hajmdagi hisob-kitoblarda tizimli siljishni kamaytiradi.

6. bool va haqiqiylik (truthiness)#

bool — ikki qiymatli tip: True va False. Qiziq fakt: bool — intning vorisi, True == 1, False == 0:

>>> True + True
2
>>> sum([True, False, True, True])    # nechta True borligini sanash
3

Bu amalda foydali: sum(pred == true for pred, true in zip(preds, labels)) — to'g'ri bashoratlar soni.

Truthiness. Har qanday obyektni shart sifatida ishlatish mumkin. Quyidagilar "yolg'on" (falsy) hisoblanadi — qolgan hammasi "rost" (truthy):

Falsy qiymatlar
False, None
0, 0.0, 0j
"" (bo'sh satr), [], (), {}, set(), range(0)
ismlar = []
if ismlar:                # "agar ro'yxat bo'sh bo'lmasa"
    print("bor")
else:
    print("bo'sh")        # bo'sh

7. None — "qiymat yo'q"#

None — "hech narsa", "qiymat yo'q" degan maxsus obyekt. U yagona (singleton): butun dasturda bitta None obyekti bor, shuning uchun is bilan tekshiriladi.

def topish(ismlar, qidirilgan):
    for i, ism in enumerate(ismlar):
        if ism == qidirilgan:
            return i
    return None   # topilmadi (buni yozmasangiz ham funksiya None qaytaradi)
 
idx = topish(["Ali", "Vali"], "Gani")
if idx is None:
    print("topilmadi")

return yozilmagan har qanday funksiya None qaytaradi. Klassik xato:

sonlar = [3, 1, 2]
sonlar = sonlar.sort()    # sort() joyida saralaydi va None qaytaradi!
print(sonlar)             # None — ro'yxat yo'qoldi

8. Tiplarni aylantirish#

int("42")         # 42
int("  42\n")     # 42 — bo'sh joylar e'tiborsiz
int(3.9)          # 3 — kesadi, yaxlitlamaydi
int("3.9")        # ValueError! Avval float("3.9"), keyin int()
int("ff", 16)     # 255 — boshqa sanoq tizimidan
float("1e-3")     # 0.001
str(3.14)         # "3.14"
bool("False")     # True! — bo'sh bo'lmagan har qanday satr truthy
list("abc")       # ['a', 'b', 'c']

bool("False") → True — bu ko'p odamni aldaydi. Konfiguratsiya fayllaridan yoki muhit o'zgaruvchilaridan kelgan satrni bool'ga shunday aylantiring:

import os
 
debug = os.environ.get("DEBUG", "0").lower() in {"1", "true", "yes", "on"}

9. Xotira haqida qisqacha: reference counting#

CPython har bir obyektga nechta nom (havola) ishora qilayotganini sanaydi. Hisoblagich 0 ga tushganda obyekt darhol o'chiriladi:

import sys
 
a = [1, 2, 3]
print(sys.getrefcount(a))   # 2 (a + getrefcount argumenti)
b = a
print(sys.getrefcount(a))   # 3
del b                       # nomni o'chiradi, obyektni emas
print(sys.getrefcount(a))   # 2

del x obyektni o'chirmaydi — faqat x nomini olib tashlaydi. Agar obyektga boshqa havola qolmasa, keyin u o'chiriladi. Bu Jupyter'da muhim: katta DataFrame'ni del df qilsangiz ham, agar Out[5] kabi joyda havola qolgan bo'lsa, xotira bo'shamaydi.

Bir-biriga ishora qiluvchi obyektlar (aylana havolalar) uchun qo'shimcha garbage collector (gc moduli) bor.

Obyekt hajmini ko'rish:

>>> import sys
>>> sys.getsizeof(1)
28          # oddiy int — 28 bayt! C'da 4-8 bayt
>>> sys.getsizeof(1.0)
24

Ha, Python'da bitta son 28 bayt egallaydi — chunki u to'liq obyekt (tip ko'rsatkichi, havolalar hisoblagichi va h.k.). Million sonli ro'yxat ≈ 36 MB; NumPy massivida esa ≈ 8 MB. Yana bitta sabab — NumPy nega kerak.

10. Bir nechta tayinlash va almashtirish#

a, b = 1, 2            # bir vaqtda ikki nom
a, b = b, a            # almashtirish — vaqtinchalik o'zgaruvchisiz
x = y = 0              # ikki nom bitta obyektga (int uchun xavfsiz)
 
# Mutable bilan ehtiyot bo'ling:
p = q = []             # p va q — BITTA ro'yxat!
p.append(1)
print(q)               # [1]
 
birinchi, *qolgan = [1, 2, 3, 4]   # birinchi=1, qolgan=[2, 3, 4]

Nima noto'g'ri ketishi mumkin#

HolatNima bo'ladiTo'g'risi
b = a bilan ro'yxatni "nusxalash"Ikki nom bitta ro'yxatga; o'zgarish ikkalasidab = a.copy() yoki b = list(a) (5-bobda chuqur nusxa)
Funksiya ichida argument ro'yxatini o'zgartirishChaqiruvchining ma'lumoti buziladiYangi obyekt qaytaring
x is 1000, s is "matn"Ba'zan True, ba'zan False; SyntaxWarning== ishlating
0.1 + 0.2 == 0.3Falsemath.isclose() yoki Decimal
x == float("nan")Har doim Falsemath.isnan(x)
value = value or default0, "", [] ham default'ga almashadiif value is None: value = default
lst = lst.sort()lst → Nonelst.sort() yoki lst = sorted(lst)
bool("False")TrueSatrni aniq qiymatlar bilan solishtiring
int("3.5")ValueErrorint(float("3.5"))
Amaliy topshiriq

1. Yorliqlarni kuzating

Quyidagi kodni bajarishdan oldin a, b, c nima bo'lishini qog'ozga yozing, keyin tekshiring:

a = [1, 2]
b = a
c = a + [3]
b.append(4)
a = a + [5]
print(a, b, c)
Maslahatni ko'rsatish

id() bilan har qadamda tekshiring. Faqat append qilingan joyda id bir xil qoladi.

Amaliy topshiriq

2. Xavfsiz normalizatsiya

normalize(data) funksiyasini yozing: har bir qiymatni (x - min) / (max - min) ga aylantirsin (min-max scaling), asl ro'yxatni o'zgartirmasin, hamma qiymat bir xil bo'lsa (max == min) nolga bo'lish xatosi bermasin (hammasini 0.0 qaytarsin), bo'sh ro'yxat uchun bo'sh ro'yxat qaytarsin.

Maslahatni ko'rsatish

max() nolga teng bo'lsa nima bo'ladi? Bo'sh ro'yxat-chi?

Amaliy topshiriq

3. Batch hisoblash

n_samples = 10_007 va batch_size = 128 uchun: nechta to'liq batch, jami nechta batch (oxirgisi to'liq bo'lmasa ham) va oxirgi batch hajmi qancha — faqat // va % bilan hisoblang.

Maslahatni ko'rsatish

-(-n // b) yoki math.ceil(). Oxirgi batch hajmi: n - (batches - 1) * b.

Amaliy topshiriq

4. Muhit o'zgaruvchisini o'qish

parse_bool(value: str | None, default: bool = False) -> bool funksiyasini yozing: "1", "true", "yes", "on" (katta-kichik harfdan qat'i nazar) → True; "0", "false", "no", "off" → False; None → default; boshqa har qanday qiymat → ValueError. Bunday funksiya har bir real loyihada kerak bo'ladi.

Yodda saqlang
  • 01Python'da hamma narsa obyekt; har obyektning id, tipi va qiymati bor.
  • 02O'zgaruvchi — obyektga yopishtirilgan nom. b = a nusxa emas, ikkinchi nom.
  • 03Mutatsiya (append, d[k] = v) obyektni o'zgartiradi va barcha nomlar buni ko'radi; = esa faqat nomni ko'chiradi.
  • 04Immutable: int, float, str, tuple, None. Mutable: list, dict, set.
  • 05is — bitta obyektmi; == — qiymat tengmi. is faqat None/True/False uchun.
  • 06float aniq emas: math.isclose, pul uchun Decimal, NaN uchun math.isnan.
  • 07// pastga yaxlitlaydi, / har doim float qaytaradi, round() juftga yaxlitlaydi.
  • 08Falsy: False, None, 0, bo'sh to'plamlar. 0 ham to'g'ri qiymat bo'lsa — is None bilan tekshiring.

Mustahkamlash

Test va amaliy masalalar

Avval testni ishlang — har javobdan keyin izoh chiqadi. Keyin masalalarni shu yerning o'zida yechib, brauzerdagi haqiqiy Python bilan avtomatik tekshiring. Natijalaringiz kurs progressi sahifasida jamlanadi.

Test: topshirilmaganMasalalar: 0/3

Bilimni tekshirish

Bob bo'yicha test

12 ta savol · har javobdan keyin to'g'ri yoki xatoligi va izoh ko'rsatiladi · oxirida natija foizda.

Amaliy masalalar

Kodni yozing va «Tekshirish»ni bosing (yoki Ctrl/⌘ + Enter). Kodingiz shu brauzerda saqlanib turadi.

01Min-max normalizatsiya

normalize(data) funksiyasini yozing: har bir qiymatni (x - min) / (max - min) ga aylantirgan yangi ro'yxat qaytarsin.

  • Asl ro'yxat o'zgarmasin.
  • Hamma qiymat bir xil bo'lsa — hammasi 0.0 bo'lsin (nolga bo'lish yo'q).
  • Bo'sh ro'yxat uchun bo'sh ro'yxat qaytsin.

02Muhit o'zgaruvchisidan bool

parse_bool(value, default=False) funksiyasini yozing:

  • "1", "true", "yes", "on" (katta-kichik harf farqsiz, chetdagi bo'sh joylar e'tiborsiz) → True
  • "0", "false", "no", "off" → False
  • None → default
  • boshqa har qanday satr → ValueError

03Batch'lar soni

batch_info(n_samples, batch_size) funksiyasi tuple qaytarsin: (to'liq_batchlar, jami_batchlar, oxirgi_batch_hajmi). Faqat // va % operatorlaridan foydalaning. Masalan, batch_info(1050, 64) → (16, 17, 26); batch_info(128, 64) → (2, 2, 64).