Ачааллж байна...
Курс 1-ийн эхний өдрөөс эхлэн та print() функцийг хэдэн зуун удаа дуудсан. Заримдаа нэг утгатай, заримдаа хоёр, гурав, эсвэл түүнээс ч олон:
python
print("Сайн байна уу")
print("Нэр:", ner)
print("Нэр:", ner, "Нас:", nas, "Хот:", hot)Гурвуулаа ажилладаг. Гэвч энэ нь өмнөх хичээлүүдэд сурсан бүх зүйлтэй зөрчилдөж байгааг анзаарсан уу? Функц нь тодорхой тооны параметртэй байдаг, хэт олон утга өгвөл TypeError гардаг гэж бид үзсэн. Тэгвэл print яагаад ХЭДЭН Ч утга хүлээж авч чаддаг юм бэ?
Тэр асуултын хариулт нь *args ба **kwargs гэсэн хоёр бичлэг юм. Тэдгээр нь функцэд "би хэдэн ч аргумент хүлээж авна" гэж хэлэх боломж олгодог. Энэ хичээл дуусахад та print-ийн нууцыг мэдэхээс гадна, өөрөө ийм уян хатан функц бичиж чаддаг болно. Мөн та бусдын кодод *args, **kwargs гэсэн бичлэг харах бүрдээ айхаа болино — тэр нь Бүлэг 10-ын decorator-т зайлшгүй хэрэгтэй болно.
Нэг зүйлийг эхнээс нь тодруулъя: args ба kwargs гэсэн НЭРС нь заавал байх ёстой зүйл биш. Python-д чухал нь од тэмдэг (* ба **) юм — нэрийг нь та *tootsuud, *utguud гэх мэтээр өөрөө сонгож болно. Гэхдээ бүх дэлхий args ба kwargs гэж нэрлэдэг заншилтай тул бид ч бас тэгнэ. Ингэснээр таны кодыг өөр хүн харахад шууд танина.
Өмнөх хичээлүүдэд функцийн параметрүүдийг "жорын орц" гэж үзсэн. Жор нь ихэвчлэн тодорхой тооны орц шаарддаг: гурван өндөг, хоёр аяга гурил.
Одоо өөр төрлийн жор төсөөлье — шөл. Шөлний жор ингэж бичигдэж болно: "усаа буцалга, дараа нь чиний гарт байгаа бүх хүнсний ногоог хий." Хэдэн ногоо байх нь хамаагүй. Нэг ч байж болно, арав ч байж болно. Жор нь тоог нь урьдчилан мэдэх шаардлагагүй, зөвхөн "бүгдийг нь хий" гэсэн заавартай.
*args бол яг тэр "бүх ногоо" гэсэн сав юм. Дуудагч хэдэн ч positional утга өгч болно, тэдгээр нь бүгд нэг л саванд цугларна.
**kwargs бол ижил санаа, гэхдээ НЭРЛЭСЭН зүйлсэд зориулагдсан. "Дараа нь чиний хүссэн бүх амтлагчийг нэр, хэмжээтэй нь хамт нэм." Давс: хоёр халбага. Перц: нэг халбага. Хэдэн ч амтлагч байж болно, тус бүр нь нэртэй.
*args-гүйгээр олон утгын нийлбэрийг тооцоолох функц бичье.
python
def niilber(a, b, c):
return a + b + c
print(niilber(1, 2, 3))6Ажиллаж байна. Гэхдээ энэ функц ЗӨВХӨН гурван тоотой ажиллана. Хоёр тоо өгвөл:
python
print(niilber(1, 2))Traceback (most recent call last):
File "main.py", line 5, in <module>
print(niilber(1, 2))
TypeError: niilber() missing 1 required positional argument: 'c'missing 1 required positional argument: 'c' гэдэг нь "c параметрт утга өгөөгүй байна" гэсэн үг. Дөрвөн тоо өгвөл ч бас алдаа гарна.
Одоохондоо та энэ асуудлыг list ашиглан шийдэж болно:
python
def niilber(tootsuud):
niit = 0
for too in tootsuud:
niit = niit + too
return niit
print(niilber([1, 2, 3]))
print(niilber([1, 2, 3, 4, 5]))6
15Энэ ажиллана. Гэхдээ дуудагч хүн ЗААВАЛ дөрвөлжин хаалт бичих хэрэгтэй болж байна: niilber([1, 2, 3]). Тэр хаалт нь тав тухгүй, мөн print(1, 2, 3) гэж хаалтгүй бичдэгтэй зөрчилдөж байна. Илүү сайхан арга байх ёстой.
python
def niilber(*args):
niit = 0
for too in args:
niit = niit + too
return niit
print(niilber(1, 2, 3))
print(niilber(1, 2, 3, 4, 5))
print(niilber(10))
print(niilber())Үр дүн:
6
15
10
0Дөрвөн өөр дуудалт, дөрвүүлээ ажиллаж байна. Хаалт бичих шаардлагагүй боллоо.
Эхний мөрийг нарийвчлан задалъя, учир нь энэ бол хичээлийн зүрх.
def niilber(*args): гэсэн бичлэгт args нэрийн өмнө нэг од байна. Тэр од нь Python-д ингэж хэлж байна: "энэ функцэд өгсөн БҮХ positional аргументыг цуглуулж, args гэдэг нэг нэрэнд өг."
Функц дотор args нь юу болох вэ? Тэр нь tuple болно. Tuple-ийг Бүлэг 5-д бүрэн үзнэ, гэхдээ одоохондоо "өөрчлөгдөшгүй жагсаалт" гэж ойлгоод өнгөрөөрэй — та түүн дээр for loop хийж чадна, len() авч чадна, index-ээр хандаж чадна. Ердийн жагсаалттай бараг адилхан.
Үүнийг өөрийн нүдээр харъя:
python
def shalgakh(*args):
print(f"args-ийн утга: {args}")
print(f"args-ийн төрөл: {type(args)}")
print(f"args-ийн урт: {len(args)}")
shalgakh(1, 2, 3)
print("---")
shalgakh("а", "б")
print("---")
shalgakh()Үр дүн:
args-ийн утга: (1, 2, 3)
args-ийн төрөл: <class 'tuple'>
args-ийн урт: 3
---
args-ийн утга: ('а', 'б')
args-ийн төрөл: <class 'str'>...Түр зогсъё — хоёр дахь дуудалтын төрөл нь str биш байх ёстой. Зөв үр дүн:
args-ийн утга: (1, 2, 3)
args-ийн төрөл: <class 'tuple'>
args-ийн урт: 3
---
args-ийн утга: ('а', 'б')
args-ийн төрөл: <class 'tuple'>
args-ийн урт: 2
---
args-ийн утга: ()
args-ийн төрөл: <class 'tuple'>
args-ийн урт: 0Гурван зүйлийг анзаараарай. Нэгдүгээрт, args нь ҮРГЭЛЖ tuple байна — өгсөн утгын төрлөөс үл хамааран. Хоёрдугаарт, дугуй хаалт (( )) нь tuple-ийн танигдах шинж. Гуравдугаарт, огт аргумент өгөөгүй ч гэсэн алдаа гарахгүй — зүгээр л ХООСОН tuple ирнэ. Энэ бол чухал: *args нь "тэг ба түүнээс дээш" аргумент гэсэн утгатай, "нэг ба түүнээс дээш" биш.
Одоо та print-ийн бүтцийг ойлгож чадна. Түүний жинхэнэ тодорхойлолт ойролцоогоор ийм харагддаг:
python
def print(*args, sep=" ", end="\n"):
...Энд бүх зүйл нийлж байна. *args нь таны өгсөн хэдэн ч утгыг цуглуулна. sep ба end нь өмнөх хичээлээс танил default утгатай keyword параметрүүд — тэдгээр нь тусгаарлагч (анхдагчаар зай) ба төгсгөл (анхдагчаар шинэ мөр) юм.
Тиймээс print("Нэр:", ner, "Нас:", nas) гэж дуудахад дөрвөн утга нь args tuple-д цугларч, print тэднийг sep буюу зайгаар холбож хэвлэдэг. Хоёр жилийн турш нууц байсан зүйл эцэст нь тайлагдлаа.
*args нь ердийн параметрүүдтэй хамт байж болно. Гэхдээ дүрэмтэй: *args нь ердийн positional параметрүүдийн АРД байрлана.
python
def bichleg(garchig, *args):
print(f"=== {garchig} ===")
for mur in args:
print(f" - {mur}")
bichleg("Хийх ажлууд", "Сүү авах", "Тайлан бичих", "Дасгал хийх")Үр дүн:
=== Хийх ажлууд ===
- Сүү авах
- Тайлан бичих
- Дасгал хийхЭхний утга ("Хийх ажлууд") нь garchig параметрт очсон. Үлдсэн БҮХ утга нь args tuple-д цугларсан. Python эхлээд ердийн параметрүүдийг дүүргэж, дараа нь үлдсэнийг нь савлана.
Энэ дүрмийг Бүлэг 2-ын star unpacking-той харьцуулж үзээрэй:
python
negdugeer, *uldsen = [1, 2, 3, 4]Тэнд ч бас "эхнийхийг ав, үлдсэнийг нь савла" гэсэн ижил санаа ажиллаж байсан. * тэмдэг нь Python-д хаана ч гарсан ижил утгатай: "үлдсэн бүхэн." Энэ бол Python-ы тууштай байдлын дахин нэг жишээ.
Одоо хоёр од руу орно. **kwargs нь *args-тай ижил санаа, гэхдээ keyword аргументуудад зориулагдсан.
python
def tokhirgoo(**kwargs):
print(f"kwargs-ийн утга: {kwargs}")
print(f"kwargs-ийн төрөл: {type(kwargs)}")
tokhirgoo(ner="Болд", nas=25, hot="Улаанбаатар")Үр дүн:
kwargs-ийн утга: {'ner': 'Болд', 'nas': 25, 'hot': 'Улаанбаатар'}
kwargs-ийн төрөл: <class 'dict'>Хамгийн чухал зүйл: kwargs нь DICTIONARY болно. args нь tuple байсан бол kwargs нь dictionary. Энэ нь логиктой — keyword аргумент бүр нь НЭР ба УТГА гэсэн хос юм, яг л dictionary-ийн key ба value шиг.
Тиймээс функц дотор та түүнийг ердийн dictionary шиг ашиглана:
python
def hun_burtgekh(**kwargs):
for tulkhuur, utga in kwargs.items():
print(f"{tulkhuur}: {utga}")
hun_burtgekh(ner="Болд", nas=25, utas="99112233")Үр дүн:
ner: Болд
nas: 25
utas: 99112233Энд Бүлэг 2-оос танил .items() ба unpacking ажиллаж байна. kwargs бол ердийн dictionary тул түүнд ердийн dictionary-ийн бүх зүйл хэрэглэгдэнэ: .items(), .keys(), .values(), len(), in шалгалт, бүр comprehension ч.
Функц дөрвөн төрлийн параметрийг зэрэг агуулж болно. Дараалал нь ХАТУУ:
python
def buren(a, b=10, *args, **kwargs):
print(f"a = {a}")
print(f"b = {b}")
print(f"args = {args}")
print(f"kwargs = {kwargs}")
buren(1)
print("---")
buren(1, 2)
print("---")
buren(1, 2, 3, 4, 5)
print("---")
buren(1, 2, 3, 4, hot="УБ", nas=25)Үр дүн:
a = 1
b = 10
args = ()
kwargs = {}
---
a = 1
b = 2
args = ()
kwargs = {}
---
a = 1
b = 2
args = (3, 4, 5)
kwargs = {}
---
a = 1
b = 2
args = (3, 4)
kwargs = {'hot': 'УБ', 'nas': 25}Python-ы дүүргэх дараалал нь ийм: эхлээд ердийн positional параметрүүдийг (a), дараа нь default-тай нь (b), дараа нь үлдсэн positional утгуудыг args-д, эцэст нь бүх нэрлэсэн утгуудыг kwargs-д.
Хатуу дараалал (санаж яваарай):
def f(ердийн, default_тай=утга, *args, **kwargs):Энэ дараалал өөрчлөгдөж болохгүй. Жишээ нь **kwargs-ыг *args-ын өмнө тавьвал:
python
def buren(a, **kwargs, *args):
pass File "main.py", line 1
def buren(a, **kwargs, *args):
^
SyntaxError: invalid syntaxЛогик нь: **kwargs бол "бүх үлдсэн нэрлэсэн зүйл" тул тэр ЗААВАЛ хамгийн сүүлд байх ёстой. Түүний ард юу ч байж болохгүй.
Одоо чухал бөгөөд эхэндээ будлиантай зүйл. Од тэмдэг нь ХОЁР өөр газарт, ХОЁР эсрэг үүрэгтэй ажилладаг.
Функцийн тодорхойлолт дотор (def f(*args)) — од нь САВЛАДАГ. Олон утгыг нэг tuple болгож цуглуулна.
Функцийн дуудалт дотор (f(*jagsaalt)) — од нь ЗАДАЛДАГ. Нэг жагсаалтыг олон тусдаа утга болгож дэлгэнэ.
Жишээгээр харъя:
python
def niilber(*args):
return sum(args)
tootsuud = [1, 2, 3, 4, 5]
print(niilber(tootsuud))Traceback (most recent call last):
File "main.py", line 8, in <module>
print(niilber(tootsuud))
TypeError: unsupported operand type(s) for +: 'int' and 'list'unsupported operand type(s) for +: 'int' and 'list' гэдэг нь "тоо болон жагсаалтыг нэмж болохгүй" гэсэн үг. Юу болов? Бид НЭГ аргумент (жагсаалт) өгсөн тул args нь ([1, 2, 3, 4, 5],) буюу "нэг жагсаалт агуулсан tuple" болсон. sum нь тэр жагсаалтыг тоо мэт нэмэх гэж оролдоод бүтэлгүйтсэн.
Одоо од тавьж, задалж өгье:
python
def niilber(*args):
return sum(args)
tootsuud = [1, 2, 3, 4, 5]
print(niilber(*tootsuud))15niilber(*tootsuud) гэсэн бичлэг нь niilber(1, 2, 3, 4, 5) гэж бичсэнтэй ЯГ ИЖИЛ утгатай. Од нь жагсаалтыг задалж, элемент бүрийг тусдаа positional аргумент болгож дамжуулсан.
Ижил зүйл dictionary дээр, хоёр одоор:
python
def hun_burtgekh(ner, nas, hot):
print(f"{ner}, {nas} настай, {hot}-д амьдардаг")
medeelel = {"ner": "Болд", "nas": 25, "hot": "Улаанбаатар"}
hun_burtgekh(**medeelel)Болд, 25 настай, Улаанбаатар-д амьдардагhun_burtgekh(**medeelel) нь hun_burtgekh(ner="Болд", nas=25, hot="Улаанбаатар") гэж бичсэнтэй яг ижил. Хоёр од нь dictionary-г задалж, key-value хос бүрийг keyword аргумент болгосон.
Санах арга: од нь ҮРГЭЛЖ "олон болгож дэлгэх" эсвэл "олноос нэг болгож цуглуулах" гэсэн санаатай. Аль тал руу явахыг байрлал нь шийднэ — тодорхойлолт дотор бол цуглуулна, дуудалт дотор бол дэлгэнэ.
Одоо *args, **kwargs-ын жинхэнэ, хамгийн түгээмэл хэрэглээ рүү орно — бөгөөд энэ нь Бүлэг 10-д decorator бичихэд яг хэрэг болно.
Заримдаа та нэг функцийг тойрч, түүнийг дуудахаас өмнө эсвэл дараа ямар нэг зүйл хийхийг хүсдэг. Гэхдээ дотоод функц нь ЯМАР аргументтай болохыг мэдэхгүй. Тэр үед *args, **kwargs нь "юу ирсэн ч бай, бүгдийг нь цааш дамжуул" гэсэн шийдэл болдог.
python
def burtgeltei_duudakh(func, *args, **kwargs):
print(f"Дуудаж байна: {func.__name__}")
ur_dun = func(*args, **kwargs)
print(f"Үр дүн: {ur_dun}")
return ur_dun
def niilber(a, b):
return a + b
def mendchilekh(ner, mendchilgee="Сайн байна уу"):
return f"{mendchilgee}, {ner}!"
burtgeltei_duudakh(niilber, 5, 3)
print("---")
burtgeltei_duudakh(mendchilekh, "Болд", mendchilgee="Өглөөний мэнд")Үр дүн:
Дуудаж байна: niilber
Үр дүн: 8
---
Дуудаж байна: mendchilekh
Үр дүн: Өглөөний мэнд, Болд!Энэ кодыг анхааралтай хараарай. burtgeltei_duudakh нь ЯМАР Ч функцийг, ЯМАР Ч аргументтай нь дуудаж чадаж байна. Тэр нь niilber-ийн хоёр тооны талаар юу ч мэдэхгүй, mendchilekh-ийн keyword параметрийн талаар ч мэдэхгүй. Тэр зөвхөн ирсэн бүхнийг цуглуулж (*args, **kwargs тодорхойлолт дээр), дараа нь бүгдийг нь дэлгэж дамжуулж байна (func(*args, **kwargs) дуудалт дээр).
Энэ хэв маягийг сайн санаж яваарай. Бүлэг 10-д та decorator бичихэд яг ийм код бичих болно — зөвхөн жаахан өөр хэлбэрээр. Тэр үед энэ хичээл рүү эргэж хараарай.
Дашрамд, func.__name__ гэсэн зүйл гарч ирлээ. Тэр нь функцийн НЭРИЙГ string болгож өгдөг. Функц бол объект бөгөөд түүнд нэр гэх мэт шинж чанарууд байдаг — энэ санаа Хичээл 3.5-д бүрэн тайлагдана.
Бүлэг 2-ын турш давтагдсан сургамж энд ч бас ажиллана: уян хатан байдал үнэ төлбөртэй ирдэг.
*args, **kwargs -ын үнэ төлбөр нь ТОДОРХОЙ БАЙДАЛ юм. Дараах хоёр функцийг харьцуулъя:
python
def hun_uusgekh(**kwargs):
...
def hun_uusgekh(ner, nas, hot="Улаанбаатар", utas=None):
...Эхний функцийг харахад та ЮУ өгөх ёстойгоо огт мэдэхгүй. Documentation унших хэрэгтэй, эсвэл кодыг нь задлах хэрэгтэй. Хоёр дахь функц нь өөрөө хэлж байна: нэр ба нас заавал, хот ба утас сонголттой.
Мөн эхний функц нь алдааг НУУНА. Хэрэв та hun_uusgekh(nre="Болд") гэж үсгийн алдаатай бичвэл **kwargs нь түүнийг дуртайяа хүлээж авна — учир нь тэр ЮУ Ч хүлээж авдаг. Алдаа зөвхөн хожим, өөр газар, ойлгомжгүй хэлбэрээр илэрнэ. Хоёр дахь функц бол шууд TypeError: got an unexpected keyword argument 'nre' гэж чанга дуугарна.
Практик дүрмүүд:
Аргументуудын тоо, нэр нь тодорхой мэдэгдэж байвал — тэдгээрийг ЯГ бичиж өг. *args, **kwargs бүү хэрэглэ.
*args нь "жинхэнэ хязгааргүй, ижил төрлийн" утгад тохирно: niilber(1, 2, 3), print(...), max(...).
**kwargs нь ихэвчлэн дамжуулах зорилгоор л ашиглагдана — та өөрөө хэрэглэхгүй, зөвхөн доод функц руу дамжуулна (decorator, wrapper).
Эргэлзвэл: тодорхой параметр бич. Тодорхой байдал үргэлж уян хатан байдлаас илүү үнэ цэнэтэй.
Хэрэв сонирхвол: def shalgakh(*args, **kwargs): print(args); print(kwargs) гэсэн функц бичээд, түүнийг олон янзаар дуудаж үзээрэй — shalgakh(1, 2, a=3), shalgakh(), shalgakh(ner="Болд"). Ямар утга хаана очиж байгааг өөрийн нүдээр харах нь энэ хичээлийн хамгийн сайн бататгал.
Мөн хүсвэл: tootsuud = [3, 1, 4, 1, 5] гэсэн жагсаалт үүсгээд, print(max(*tootsuud)) ба print(max(tootsuud)) хоёрыг харьцуулж үзээрэй. Хоёулаа ажиллана, гэхдээ огт өөр замаар — max нь *args хүлээж авдаг ч жагсаалтыг ч бас ойлгодог.
1. Дарааллыг зөрчих
python
def buren(*args, a, **kwargs):
pass
buren(1, 2, 3)Traceback (most recent call last):
File "main.py", line 5, in <module>
buren(1, 2, 3)
TypeError: buren() missing 1 required keyword-only argument: 'a'missing 1 required keyword-only argument: 'a' гэдэг нь "a нь зөвхөн keyword-оор дуудагдах ёстой боловч утга өгөгдөөгүй" гэсэн үг. *args-ын АРД байгаа параметр нь автоматаар "зөвхөн keyword" болдог, учир нь бүх positional утгууд аль хэдийн args-д цугларчихсан байна. Засвар: ердийн параметрүүдийг *args-ын ӨМНӨ тавь.
2. Од мартаж, жагсаалтыг бүхлээр нь дамжуулах
python
def niilber(*args):
return sum(args)
print(niilber([1, 2, 3]))TypeError: unsupported operand type(s) for +: 'int' and 'list'Дээр дэлгэрэнгүй үзсэн. Засвар: niilber(*[1, 2, 3]) эсвэл niilber(1, 2, 3).
3. kwargs-ыг tuple гэж бодох
python
def tokhirgoo(**kwargs):
print(kwargs[0])
tokhirgoo(ner="Болд")Traceback (most recent call last):
File "main.py", line 2, in tokhirgoo
print(kwargs[0])
KeyError: 0KeyError: 0 гэдэг нь "0 гэсэн түлхүүр dictionary-д байхгүй" гэсэн үг. kwargs нь DICTIONARY, tuple биш — тиймээс index-ээр хандаж болохгүй, зөвхөн НЭРЭЭР хандана: kwargs["ner"]. Санах арга: нэг од → tuple, хоёр од → dictionary.
4. Дуудалт дээр од мартах (dictionary дамжуулахад)
python
def hun_burtgekh(ner, nas):
print(ner, nas)
medeelel = {"ner": "Болд", "nas": 25}
hun_burtgekh(medeelel)Traceback (most recent call last):
File "main.py", line 7, in <module>
hun_burtgekh(medeelel)
TypeError: hun_burtgekh() missing 1 required positional argument: 'nas'Dictionary-г БҮХЭЛД нь нэг аргумент болгож дамжуулсан тул ner -т dictionary очиж, nas -д юу ч очоогүй. Засвар: hun_burtgekh(**medeelel) — хоёр одоор задалж дамжуул.
*args нь дурын тооны positional аргументыг цуглуулж, функц дотор tuple болгодог.
**kwargs нь дурын тооны keyword аргументыг цуглуулж, функц дотор dictionary болгодог.
args ба kwargs гэсэн нэрс нь заншил; чухал нь од тэмдэг өөрөө.
Хатуу дараалал: def f(ердийн, default=утга, *args, **kwargs).
Од тэмдэг ХОЁР эсрэг үүрэгтэй: тодорхойлолт дээр САВЛАНА, дуудалт дээр ЗАДАЛНА. f(*jagsaalt) нь жагсаалтыг тусдаа аргумент болгож дэлгэнэ; f(**dictionary) нь key-value хосыг keyword болгож дэлгэнэ.
print нь def print(*args, sep=" ", end="\n") бүтэцтэй — тиймээс хэдэн ч утга хүлээж авдаг.
Хамгийн чухал хэрэглээ нь дамжуулах: func(*args, **kwargs) — юу ирсэн ч бай, бүгдийг нь цааш дамжуулах. Энэ хэв маяг Бүлэг 10-ын decorator-т зайлшгүй хэрэгтэй.
Аргументууд нь тодорхой мэдэгдэж байвал тэдгээрийг ЯГ бич. *args, **kwargs нь тодорхой байдлыг золиослодог — зөвхөн үнэхээр хэрэгтэй үед л ашигла.
Дараагийн хичээлд бид функцийн ДОТООД ертөнц рүү орно. Функц дотор үүсгэсэн хувьсагч гаднаа юу болдог вэ? Гаднах хувьсагчийг функц дотроос уншиж болох уу, өөрчилж болох уу? Тэр асуултуудын хариулт нь scope гэдэг ойлголт бөгөөд түүнийг ойлгохгүйгээр Бүлэг 10-ын closure ба decorator ойлгогдохгүй. Мөн тэнд UnboundLocalError гэсэн нэг алдартай, төөрөгдүүлэгч алдааг задлан тайлбарлана.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.