Ачааллж байна...
Бүлэг 8-ийн турш би нэг зүйлийг дахин дахин хэлсэн: энэ бүх код нь ЯДАРГААТАЙ.
Одоо тэр ядаргааг өөрийн нүдээр хараарай. Энгийн Zarlaga class бичье — зөвхөн гурван талбартай.
python
class Zarlaga:
def __init__(self, ner: str, dun: int, angilal: str) -> None:
self.ner = ner
self.dun = dun
self.angilal = angilal
def __repr__(self) -> str:
return (
f"Zarlaga(ner={self.ner!r}, dun={self.dun}, "
f"angilal={self.angilal!r})"
)
def __eq__(self, busad: object) -> bool:
if not isinstance(busad, Zarlaga):
return NotImplemented
return (
self.ner == busad.ner
and self.dun == busad.dun
and self.angilal == busad.angilal
)
def __hash__(self) -> int:
return hash((self.ner, self.dun, self.angilal))Хорин мөр. Гурван талбарын төлөө.
Одоо тэр кодыг анхааралтай хараарай. Тэнд ЯМАР Ч тархи ажилласан уу?
__init__ нь зөвхөн self.x = x гэж гурван удаа бичсэн
__repr__ нь зөвхөн талбар бүрийн нэр, утгыг жагсаасан
__eq__ нь зөвхөн талбар бүрийг харьцуулсан
__hash__ нь зөвхөн талбаруудыг tuple болгосон
БҮГД МЕХАНИК. Ямар ч бүтээлч шийдвэр байхгүй. Хэрэв би талбар нэмбэл — дөрвөн method бүгдэд нэмэх хэрэгтэй.
Программын ертөнцөд ийм кодыг boilerplate гэж нэрлэдэг. Тэр нь "заавал бичих ёстой, гэхдээ ямар ч утга нэмдэггүй" код гэсэн утгатай. (Нэр нь хэвлэлийн үйлдвэрлэлээс гаралтай — өмнө нь сонин хэвлэхэд өөрчлөгддөггүй хэсгүүдийг металл хавтан дээр урьдчилан цутгадаг байсан.)
Boilerplate нь хоёр асуудалтай:
Цаг иддэг. Хорин мөр бичихийн оронд та жинхэнэ ажилдаа зарцуулж болох байсан.
Алдаа үүсгэдэг. Дөрвөн method-д ижил талбарууд давтагдаж байгаа тул нэгийг мартах, нэгийг буруу бичих эрсдэл өндөр. Тэр алдаа нь ЧИМЭЭГҮЙ байдаг.
dataclass нь энэ бүхнийг АВТОМАТААР үүсгэнэ.
python
from dataclasses import dataclass
@dataclass
class Zarlaga:
ner: str
dun: int
angilal: strДөрвөн мөр. Хорин мөрийн оронд.
Тэгээд ажиллуулъя:
python
from dataclasses import dataclass
@dataclass
class Zarlaga:
ner: str
dun: int
angilal: str
z1 = Zarlaga("Ном", 15000, "боловсрол")
z2 = Zarlaga("Ном", 15000, "боловсрол")
z3 = Zarlaga("Автобус", 1000, "тээвэр")
print(z1)
print(z1 == z2)
print(z1 == z3)
print(z1.dun)Zarlaga(ner='Ном', dun=15000, angilal='боловсрол')
True
False
15000БҮГД АЖИЛЛАЖ БАЙНА.
__init__ — Zarlaga("Ном", 15000, "боловсрол") ажиллаж байна
__repr__ — print(z1) нь гоё харагдаж байна
__eq__ — z1 == z2 нь True
Бид тэдгээрийн НЭГИЙГ Ч бичээгүй. @dataclass нь тэднийг АВТОМАТААР үүсгэсэн.
Байшин барихыг төсөөлье.
Хуучин арга: та тоосго нэг нэгээр нь өрнө. Ханын үндэс, өнцөг, цонхны нүх — бүгдийг гараар. Хэдэн долоо хоног зарцуулна. Ажил нь механик, гэхдээ алдаа гаргах боломж их.
Орчин үеийн арга: та бэлэн хана захиална. Үйлдвэрт цутгаж, ачаад авчирна. Та зөвхөн "надад гурван хана, хоёр цонхтой" гэж хэлнэ. Хэдэн цагт байшин босно.
Гол зүйл: бэлэн хана нь таны гараар барих байсан хананаас ЯЛГААГҮЙ. Тэр нь ижил тоосго, ижил бетон. Зөвхөн та түүнийг гараар өрөх шаардлагагүй болсон.
dataclass бол яг тэр. Тэр нь илбэ БИШ. Тэр нь таны бичих байсан ЯГ ТЭР кодыг автоматаар бичиж өгч байна. Үр дүн нь ердийн class — ердийн __init__, ердийн __repr__, ердийн __eq__.
Яг тиймээс би Бүлэг 8-ийг бичсэн. Хэрэв та тэдгээр method-уудыг гараар бичиж үзээгүй бол, dataclass нь илбэ мэт санагдана. Одоо та түүнийг харахдаа "аан, тэр нь миний бичих байсан кодыг л бичиж байна" гэж мэдэж байна.
@dataclass -ыг задалж харахБүлэг 8.4-т би @ тэмдэг нь decorator гэж хэлсэн. Одоо түүнийг дахин сануулъя:
python
@dataclass
class Zarlaga:
...Энэ мөр нь ойролцоогоор дараах кодтой ижил утгатай:
python
class Zarlaga:
...
Zarlaga = dataclass(Zarlaga)dataclass бол ФУНКЦ. Тэр нь class-ыг АВЧ, түүнийг ХАРАХ, дараа нь method нэмж, буцаана.
Юуг харах вэ? Талбаруудын hint-ийг.
python
@dataclass
class Zarlaga:
ner: str
dun: int
angilal: strdataclass нь ner: str, dun: int, angilal: str гэсэн гурван hint-ийг ОЛЖ УНШИНА. Дараа нь тэдгээрийг ашиглан __init__, __repr__, __eq__ -ыг үүсгэнэ.
Одоо энэ бол чухал өгүүлбэр:
Type hint нь эцэст нь ЖИНХЭНЭ АЖИЛ хийж эхэллээ.
Бүлэг 4-т би "Python нь type hint-ийг ҮЛ ТООМСОРЛОДОГ, тэдгээр нь зөвхөн хүн ба хэрэгслүүдэд зориулагдсан" гэж хэлсэн. Тэр үнэн байсан.
Гэвч би мөн "зарим framework нь hint-ийг УНШИЖ, ажиллуулдаг" гэж хэлсэн. dataclass бол яг тэр. Тэр нь таны бичсэн hint-ийг уншиж, түүн дээр үндэслэн код үүсгэж байна.
Хэрэв та hint бичээгүй бол — dataclass ажиллахгүй:
python
@dataclass
class Zarlaga:
ner
dun
angilalTraceback (most recent call last):
File "main.py", line 5, in <module>
class Zarlaga:
File "main.py", line 6, in Zarlaga
ner
NameError: name 'ner' is not definedHint нь ЗААВАЛ хэрэгтэй. Тэдгээргүйгээр dataclass нь ямар талбар байгааг мэдэхгүй.
Энэ бол Бүлэг 15-д харах зүйлийн урьдчилсан амт. FastAPI нь ЯГ ижил зарчмаар ажилладаг — тэр нь таны type hint-ийг уншиж, түүн дээр үндэслэн бүх зүйлийг автоматаар бүтээдэг.
Одоо анхаарах зүйл. Анхдагчаар dataclass нь __hash__ -ыг үүсгэдэггүй.
python
from dataclasses import dataclass
@dataclass
class Zarlaga:
ner: str
dun: int
angilal: str
zarlaguud = {Zarlaga("Ном", 15000, "боловсрол")}Traceback (most recent call last):
File "main.py", line 11, in <module>
zarlaguud = {Zarlaga("Ном", 15000, "боловсрол")}
TypeError: unhashable type: 'Zarlaga'TypeError: unhashable type: 'Zarlaga' — Бүлэг 8.2-оос танил алдаа!
Тэнд бид "__eq__ бичсэн class нь автоматаар hashable биш болно" гэж үзсэн. dataclass нь __eq__ -ыг үүсгэсэн тул ижил дүрэм ажиллаж байна.
Яагаад dataclass нь __hash__ -ыг ч бас үүсгэдэггүй вэ? Учир нь dataclass-ын талбарууд нь ӨӨРЧЛӨГДӨЖ чадна:
python
z = Zarlaga("Ном", 15000, "боловсрол")
z.dun = 20000 # өөрчлөгдлөө!
print(z)Zarlaga(ner='Ном', dun=20000, angilal='боловсрол')Хэрэв объект нь өөрчлөгддөг бол — түүний hash ч бас өөрчлөгдөх ёстой. Гэвч set нь түүнийг ХУУЧИН hash-аар нь хадгалсан. Одоо set нь түүнийг ОЛОХОО БОЛИНО. Өгөгдөл алга болно.
Тиймээс Python нь болгоомжтой ханддаг: өөрчлөгддөг объект бол hashable биш.
Шийдэл: frozen=True.
python
from dataclasses import dataclass
@dataclass(frozen=True)
class Zarlaga:
ner: str
dun: int
angilal: str
zarlaguud: set[Zarlaga] = {
Zarlaga("Ном", 15000, "боловсрол"),
Zarlaga("Ном", 15000, "боловсрол"), # ДАВХАРДАЛ
Zarlaga("Автобус", 1000, "тээвэр"),
}
print(len(zarlaguud))
for z in zarlaguud:
print(z)2
Zarlaga(ner='Автобус', dun=1000, angilal='тээвэр')
Zarlaga(ner='Ном', dun=15000, angilal='боловсрол')Set ажиллаж байна, давхардал устлаа.
@dataclass(frozen=True) нь хоёр зүйл хийдэг:
1. Объектыг ӨӨРЧЛӨГДӨШГҮЙ (immutable) болгоно.
python
z = Zarlaga("Ном", 15000, "боловсрол")
z.dun = 20000Traceback (most recent call last):
File "main.py", line 2, in <module>
z.dun = 20000
dataclasses.FrozenInstanceError: cannot assign to field 'dun'FrozenInstanceError: cannot assign to field 'dun' — "dun талбарт утга олгож болохгүй." Объект нь хөлдөөгдсөн (frozen).
2. __hash__ -ыг АВТОМАТААР үүсгэнэ. Учир нь объект өөрчлөгдөхгүй тул hash нь ч бас өөрчлөгдөхгүй — аюулгүй.
Бүлэг 5.1-ийн tuple -ийн санааг санаж байна уу? Тэнд бид "immutable нь давуу тал" гэж үзсэн: аюулгүй байдал, dictionary-ийн key болж чадна, зорилгыг илэрхийлдэг.
frozen=True нь class-д яг тэр гурван давуу талыг өгч байна.
frozen=True ашигла:
Объект нь ӨГӨГДӨЛ илэрхийлж байвал, төлөв биш (зарлага, координат, огноо, тохиргоо)
Set-д хийх эсвэл dictionary-ийн key болгох хэрэгтэй бол
Объект нь үүссэний дараа өөрчлөгдөх ЁСГҮЙ бол
Функцэд дамжуулахад аюулгүй байлгах хэрэгтэй бол (функц түүнийг чимээгүй өөрчилж чадахгүй)
frozen=True БҮҮ ашигла:
Объект нь ТӨЛӨВТЭЙ бол (Nom нь zeeldsen төлөвтэй — тэр нь өөрчлөгдөх ЁСТОЙ)
Объектыг ажиллах явцад өөрчлөх шаардлагатай бол
Практик зөвлөгөө: эргэлзвэл frozen=True -ээр эхэл. Хэрэв өөрчлөх шаардлага гарвал — тэр үед авч хаяарай. Immutable-аас mutable руу шилжих нь эсрэгээсээ хамаагүй хялбар.
Одоо энэ бүлгийн хамгийн сэтгэл ханамжтай мөч ирлээ.
Бүлэг 6.5-ийн guided build-ыг санаж байна уу? Тэнд бид dict[str, str | int] ашигласан бөгөөд тэр нь int(z["dun"]) гэсэн муухай хувиргалтуудыг ХАА САЙГҮЙ шаардаж байсан:
python
# Бүлэг 6-ийн код
Zarlaga = dict[str, str | int]
niit = sum(int(z["dun"]) for z in zarlaguud)
erembelsen = sorted(zarlaguud, key=lambda z: int(z["dun"]), reverse=True)
bulgeer[angilal] = bulgeer.get(angilal, 0) + int(z["dun"])Тэр int(...) бүр нь editor-ыг тайвшруулах гэсэн оролдлого байсан. Бид өөрсдөө мэдэж байсан — dun нь ҮРГЭЛЖ int. Гэвч hint нь тэр мэдлэгийг илэрхийлж чадаагүй.
Одоо dataclass -аар:
python
from dataclasses import dataclass
@dataclass(frozen=True)
class Zarlaga:
ner: str
dun: int
angilal: str
zarlaguud: list[Zarlaga] = [
Zarlaga("Ном", 15000, "боловсрол"),
Zarlaga("Автобус", 1000, "тээвэр"),
Zarlaga("Хоол", 8500, "хоол"),
Zarlaga("Ном", 22000, "боловсрол"),
]
niit = sum(z.dun for z in zarlaguud)
erembelsen = sorted(zarlaguud, key=lambda z: z.dun, reverse=True)
bulgeer: dict[str, int] = {}
for z in zarlaguud:
bulgeer[z.angilal] = bulgeer.get(z.angilal, 0) + z.dun
print(f"Нийт: {niit:,} төгрөг")
print(f"Хамгийн үнэтэй: {erembelsen[0].ner} — {erembelsen[0].dun:,}")
print(f"Ангилалаар: {bulgeer}")Нийт: 46,500 төгрөг
Хамгийн үнэтэй: Ном — 22,000
Ангилалаар: {'боловсрол': 37000, 'тээвэр': 1000, 'хоол': 8500}БҮХ int(...) АРИЛЛАА.
Ялгааг хажуу хажуугаар нь харъя:
Бүлэг 6 (dictionary) | Бүлэг 9 (dataclass) |
|---|---|
|
|
|
|
|
|
|
|
Editor: "энэ | Editor: "энэ ҮРГЭЛЖ |
|
|
Дөрвөн давуу тал:
1. Код нь БОГИНО. int(...) бүр алга болсон.
2. Editor нь БҮРЭН тусална. z. гэж бичихэд ner, dun, angilal гурвуулаа санал болгогдоно. Мөн z.dun. гэж бичихэд int-ийн method-ууд гарч ирнэ.
3. Үсгийн алдаа ШУУД илэрнэ. z["duun"] гэсэн алдаа нь dictionary-д АЖИЛЛАХ ҮЕД л илэрдэг (KeyError). z.duun нь editor-т ШУУД улаанаар зурагдана.
4. Талбар нэмэхэд бүх зүйл шинэчлэгдэнэ. Dataclass-д нэг мөр нэмбэл — __init__, __repr__, __eq__ бүгд автоматаар шинэчлэгдэнэ.
Энэ бол Бүлэг 4-т хэлсэн зүйлийн биелэл: "hint нь МУУХАЙ болж эхэлбэл (dict[str, str | int]), тэр нь илүү сайн бүтэц хэрэгтэй байгаагийн дохио." Тэр илүү сайн бүтэц нь dataclass байсан юм.
Dataclass бол ЖИНХЭНЭ CLASS. Тэр нь ердийн class-ын БҮХ чадвартай — та түүнд method нэмж, property нэмж, inheritance ашиглаж болно.
python
from dataclasses import dataclass
@dataclass(frozen=True)
class Zarlaga:
ner: str
dun: int
angilal: str
@property
def tom_zarlaga_uu(self) -> bool:
return self.dun > 10000
def khuvi_tootsoolokh(self, niit: int) -> float:
if niit == 0:
return 0.0
return self.dun / niit * 100
def __str__(self) -> str:
temdeg = "!" if self.tom_zarlaga_uu else " "
return f"{temdeg} {self.ner:<12} {self.dun:>10,} ({self.angilal})"
zarlaguud = [
Zarlaga("Ном", 15000, "боловсрол"),
Zarlaga("Автобус", 1000, "тээвэр"),
Zarlaga("Хоол", 8500, "хоол"),
]
niit = sum(z.dun for z in zarlaguud)
for z in zarlaguud:
print(f"{z} — {z.khuvi_tootsoolokh(niit):.1f}%")! Ном 15,000 (боловсрол) — 61.2%
Автобус 1,000 (тээвэр) — 4.1%
Хоол 8,500 (хоол) — 34.7%Property, method, __str__ — бүгд ажиллаж байна.
Анзаараарай: бид __str__ -ыг ГАРААР бичсэн. dataclass нь __repr__ -ыг үүсгэдэг ч __str__ -ыг үүсгэдэггүй. Тиймээс хэрэглэгчид зориулсан тусгай хэлбэр хэрэгтэй бол — өөрөө бич.
Гэхдээ __repr__ нь хэвээрээ ажиллана:
python
print(repr(zarlaguud[0]))
print(zarlaguud)Zarlaga(ner='Ном', dun=15000, angilal='боловсрол')
[Zarlaga(ner='Ном', dun=15000, angilal='боловсрол'), Zarlaga(ner='Автобус', dun=1000, angilal='тээвэр'), Zarlaga(ner='Хоол', dun=8500, angilal='хоол')]Хоёулаа зэрэг: __str__ нь гоё (хэрэглэгчид), __repr__ нь техникийн (дебагт). Бүлэг 8.1-ийн санаа хэвээрээ.
@dataclass нь хэдэн keyword argument хүлээж авдаг (Бүлэг 3.2-ын мэдлэг ажиллаж байна):
python
@dataclass # анхдагч
@dataclass(frozen=True) # өөрчлөгдөшгүй + hashable
@dataclass(order=True) # <, >, <=, >= операторууд ажиллана
@dataclass(frozen=True, order=True) # хоёулааorder=True нь сонирхолтой:
python
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class Zarlaga:
dun: int # ЭХНИЙ талбар — эрэмбэлэлт үүгээр явна
ner: str
angilal: str
zarlaguud = [
Zarlaga(15000, "Ном", "боловсрол"),
Zarlaga(1000, "Автобус", "тээвэр"),
Zarlaga(8500, "Хоол", "хоол"),
]
for z in sorted(zarlaguud):
print(z)Zarlaga(dun=1000, ner='Автобус', angilal='тээвэр')
Zarlaga(dun=8500, ner='Хоол', angilal='хоол')
Zarlaga(dun=15000, ner='Ном', angilal='боловсрол')sorted() нь key= -гүйгээр ажиллалаа!
order=True нь __lt__, __gt__ гэх мэт харьцуулах method-уудыг үүсгэдэг. Тэдгээр нь талбаруудыг ДАРААЛЛААР нь харьцуулна — эхлээд эхний талбар, тэнцүү бол хоёр дахь.
Тиймээс би dun -ыг ЭХЭНД тавьсан. Хэрэв ner эхэнд байсан бол — цагаан толгойгоор эрэмбэлэгдэх байсан.
Практик зөвлөгөө: order=True нь тохиромжтой байж болох ч, ихэнхдээ sorted(zarlaguud, key=lambda z: z.dun) гэж бичих нь илүү ТОД. Тэр нь юугаар эрэмбэлж байгааг ХЭЛЖ байна. order=True нь түүнийг нуудаг.
Тиймээс энэ курс нь order=True -ыг АШИГЛАХГҮЙ. Гэхдээ бусдын кодод харах магадлалтай тул мэдэж байгаарай.
Хэрэв сонирхвол: Бүлэг 8-ийн эхэнд бичсэн 20 мөрийн Zarlaga class-ыг өөрөө бичээд, дараа нь @dataclass -аар 4 мөр болгож солиод, хоёулангийнх нь зан төлөв ЯГ ИЖИЛ гэдгийг өөрөө шалгаарай. print(), ==, талбарт хандах — бүгд ижил ажиллана.
Дараа нь frozen=True нэмээд, z.dun = 999 гэж оролдоод FrozenInstanceError -ыг өөрийн нүдээр хараарай. Дараа нь set-д хийж, ажиллахыг нь хараарай.
Мөн хүсвэл: Бүлэг 6-ийн зарлагын бүртгэлийн программаа нээгээд, dict -ийг dataclass болгож дахин бичээд үзээрэй. Бүх int(...) алга болохыг мэдрэх нь энэ бүлгийн хамгийн сэтгэл ханамжтай мөч байх болно.
1. import мартах
python
@dataclass
class Zarlaga:
ner: strNameError: name 'dataclass' is not definedfrom dataclasses import dataclass — модулийн нэр нь олон тооны (dataclasses), decorator-ын нэр нь ганц тооны (dataclass). Энэ ялгаа нь эхлэгчдийг байнга төөрөгдүүлдэг.
2. Type hint мартах
python
@dataclass
class Zarlaga:
ner
dunNameError: name 'ner' is not definedHint нь ЗААВАЛ. dataclass нь тэдгээрийг УНШИЖ ажилладаг.
3. @ мартах
python
dataclass
class Zarlaga:
ner: str
dun: intАлдаа гарахгүй! Гэвч class нь ердийн class хэвээрээ:
python
z = Zarlaga("Ном", 15000)TypeError: Zarlaga() takes no argumentstakes no arguments — __init__ үүсээгүй. @ нь ЗААВАЛ хэрэгтэй.
4. Set-д хийхийг оролдох (frozen-гүй)
TypeError: unhashable type: 'Zarlaga'@dataclass(frozen=True) нэм.
5. Frozen объектыг өөрчлөх
dataclasses.FrozenInstanceError: cannot assign to field 'dun'Frozen нь ЗОРИУДААР. Хэрэв өөрчлөх шаардлагатай бол — frozen -ыг ав. Эсвэл ШИНЭ объект үүсгэ:
python
z_shine = Zarlaga(z.ner, 20000, z.angilal)(Python-д dataclasses.replace() гэсэн туслах функц бий, тэр нь энэ ажлыг хялбарчилдаг — гэхдээ энэ курсэд шаардлагагүй.)
6. __str__ автоматаар үүснэ гэж бодох
dataclass нь __repr__ -ыг үүсгэдэг, __str__ -ыг ҮГҮЙ. Гэхдээ Бүлэг 8.1-ээс санаж байгаа байх: __str__ байхгүй бол Python нь __repr__ -ыг ашиглана. Тиймээс print(z) нь ажиллана — зөвхөн техникийн хэлбэрээр.
Boilerplate гэдэг нь "заавал бичих ёстой, гэхдээ утга нэмдэггүй" механик код. __init__, __repr__, __eq__, __hash__ — бүгд boilerplate.
@dataclass нь тэдгээрийг АВТОМАТААР үүсгэнэ. 20 мөр → 4 мөр.
from dataclasses import dataclass — модуль нь олон тоо, decorator нь ганц тоо.
dataclass бол илбэ БИШ. Тэр нь таны бичих байсан ЯГ ТЭР кодыг автоматаар бичиж байна.
Type hint нь ЗААВАЛ хэрэгтэй. dataclass нь тэднийг УНШИЖ ажилладаг — hint нь эцэст нь жинхэнэ ажил хийж эхэллээ.
Анхдагчаар dataclass нь __init__, __repr__, __eq__ үүсгэнэ. __hash__ -ыг ҮҮСГЭХГҮЙ (объект өөрчлөгддөг тул).
@dataclass(frozen=True) нь: (1) объектыг immutable болгоно, (2) __hash__ -ыг үүсгэнэ → set, dictionary-key ажиллана.
Frozen ашигла: объект нь ӨГӨГДӨЛ илэрхийлж байвал. БҮҮ ашигла: объект нь ТӨЛӨВТЭЙ бол (zeeldsen).
Эргэлзвэл frozen=True -ээр эхэл.
Dataclass бол ЖИНХЭНЭ CLASS — method, property, inheritance бүгд ажиллана. __str__ -ыг өөрөө бичиж болно.
dict[str, str | int] → dataclass: бүх int(...) хувиргалт арилна, editor бүрэн тусална, үсгийн алдаа ШУУД илэрнэ.
Дараагийн хичээлд бид @dataclass -ыг илүү гүнзгий ашиглана: default утга (Бүлэг 3.1-ийн мэдлэг class дээр), field() функц, мөн mutable default-ын урхи (Бүлэг 3.1-ээс танил асуудал, гэхдээ шинэ хэлбэрээр). Тэнд би мөн нэг чухал зүйлийг чангаар хэлнэ: dataclass + type hint + default утга гэсэн ГУРВАЛСАН нь ЯГ ТЭР хэлбэр бөгөөд FastAPI-гийн Pydantic ашигладаг. Тэр нь санамсаргүй биш — тэр бол энэ курсийн бүх замын зорилго юм.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.