Dekoratorlar bilan tanishish
Pythonda, dekorator — funksiyani boshqa funksiya ichiga o'rab (wrapping), uning funksionalligini o'zgartirish imkonini beruvchi dizayn patterni (design pattern).
Tashqi funksiya dekorator deb ataladi, u asl funksiyani argument sifatida oladi va uning o'zgartirilgan versiyasini qaytaradi.
Dekoratorlar haqida o'rganishdan oldin, Python funksiyalariga oid bir necha muhim tushunchani tushunishimiz kerak. Shuningdek, Pythonda hamma narsa, hatto funksiyalar ham, obyekt ekanligini yodda tuting.
Ichma-ich funksiya
Biz bitta funksiyani boshqasi ichiga joylashtirishimiz mumkin, bu ichma-ich funksiya (nested function) deb ataladi. Masalan,
def outer(x):
def inner(y):
return x + y
return inner
add_five = outer(5)
result = add_five(6)
print(result) # 11 ni chop etadi
# Natija: 11
Bu yerda, biz inner() funksiyasini outer() funksiyasi ichida yaratdik.
Funksiyani argument sifatida uzatish
Pythonda funksiyani boshqa funksiyaga argument sifatida uzatishimiz mumkin. Masalan,
def add(x, y):
return x + y
def calculate(func, x, y):
return func(x, y)
result = calculate(add, 4, 6)
print(result) # 10 ni chop etadi
Natija
10
Yuqoridagi misolda, calculate() funksiyasi funksiyani o'zining argumenti sifatida oladi. calculate()ni chaqirayotganda, biz add() funksiyasini argument sifatida uzatyapmiz.
calculate() funksiyasida, argumentlar: func, x, y mos ravishda add, 4 va 6ga aylanadi.
Va shuning uchun, func(x, y) add(4, 6)ga aylanadi, bu esa 10ni qaytaradi.
Funksiyani qiymat sifatida qaytarish
Pythonda, biz funksiyani qaytarilgan qiymat sifatida ham qaytarishimiz mumkin. Masalan,
def greeting(name):
def hello():
return "Hello, " + name + "!"
return hello
greet = greeting("Atlantis")
print(greet()) # "Hello, Atlantis!" ni chop etadi
# Natija: Hello, Atlantis!
Yuqoridagi misolda, return hello operatori ichki hello() funksiyasini qaytaradi. Bu funksiya endi greet o'zgaruvchisiga berilgan.
Shuning uchun, greet()ni funksiya sifatida chaqirganimizda, natijani olamiz.
Ilgari aytib o'tilganidek, Python dekoratori — funksiyani oladigan va unga ba'zi funksionallik qo'shib qaytaradigan funksiya.
Aslida, maxsus __call__() metodini amalga oshiradigan har qanday obyekt chaqiriladigan (callable) deb ataladi. Shunday qilib, eng sodda ma'noda, dekorator — chaqiriladiganni qaytaruvchi chaqiriladigan.
Asosan, dekorator funksiyani oladi, unga ba'zi funksionallik qo'shadi va uni qaytaradi.
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
# Natija: I am ordinary
Bu yerda, biz ikkita funksiya yaratdik:
"I am ordinary"ni chop etuvchi ordinary()inner() nomli ichma-ich funksiyaga ega bo'lgan, ichki funksiyani qaytaruvchi make_pretty().Biz ordinary() funksiyasini oddiy tarzda chaqiryapmiz, shuning uchun "I am ordinary" natijasini olamiz. Endi, keling uni dekorator funksiyasi yordamida chaqiramiz.
def make_pretty(func):
# ichki funksiyani aniqlash
def inner():
# dekoratsiyalangan funksiyaga qo'shimcha xatti-harakat qo'shish
print("I got decorated")
# asl funksiyani chaqirish
func()
# ichki funksiyani qaytarish
return inner
# oddiy funksiyani aniqlash
def ordinary():
print("I am ordinary")
# oddiy funksiyani dekoratsiyalash
decorated_func = make_pretty(ordinary)
# dekoratsiyalangan funksiyani chaqirish
decorated_func()
Natija
I got decorated
I am ordinary
Yuqorida ko'rsatilgan misolda, make_pretty() — dekorator. Kodga e'tibor bering,
decorated_func = make_pretty(ordinary)
ordinary() funksiyasini make_pretty()ga argument sifatida uzatyapmiz.make_pretty() funksiyasi ichki funksiyani qaytaradi, va u endi decorated_func o'zgaruvchisiga berilgan.decorated_func()
Bu yerda, biz aslida inner() funksiyasini chaqiryapmiz, u yerda biz chop etyapmiz
Funksiya chaqiruvini o'zgaruvchiga berish o'rniga, Python @ belgisi yordamida bu funksionallikka erishishning ancha nafisroq usulini taqdim etadi. Masalan,
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
@make_pretty
def ordinary():
print("I am ordinary")
ordinary()
Natija
I got decorated
I am ordinary
Bu yerda, ordinary() funksiyasi @make_pretty sintaksisi yordamida make_pretty() dekoratori bilan dekoratsiyalangan, bu ordinary = make_pretty(ordinary)ni chaqirishga teng.
Yuqoridagi dekorator oddiy edi va u faqat hech qanday parametrga ega bo'lmagan funksiyalar bilan ishlar edi. Agar bizda quyidagi kabi parametrlarni qabul qiluvchi funksiyalar bo'lsa nima bo'ladi:
def divide(a, b):
return a/b
Bu funksiya ikkita parametrga ega, a va b. Bilamizki, agar b ni 0 sifatida uzatsak, u xatolik beradi.
Endi keling, xatolikka sabab bo'ladigan bu holatni tekshiradigan dekorator yasaymiz.
def smart_divide(func):
def inner(a, b):
print("I am going to divide", a, "and", b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a, b)
return inner
@smart_divide
def divide(a, b):
print(a/b)
divide(2,5)
divide(2,0)
Natija
I am going to divide 2 and 5
0.4
I am going to divide 2 and 0
Whoops! cannot divide
Bu yerda, divide() funksiyasini (2,5) argumentlari bilan chaqirganimizda, buning o'rniga smart_divide() dekoratorida aniqlangan inner() funksiyasi chaqiriladi.
Bu inner() funksiyasi asl divide() funksiyasini 2 va 5 argumentlari bilan chaqiradi va natijani, ya'ni 0.4ni qaytaradi.
Xuddi shunday, divide() funksiyasini (2,0) argumentlari bilan chaqirganimizda, inner() funksiyasi bning 0ga tengligini tekshiradi va None qaytarishdan oldin xato xabarini chop etadi.
Pythonda bir nechta dekoratorni zanjirlash mumkin.
Pythonda dekoratorlarni zanjirlash uchun, biz bitta funksiyaga bir nechta dekoratorni, ularni bir-birining ustiga joylashtirib, eng ichkisi birinchi bo'lib qo'llanadigan tarzda qo'llashimiz mumkin.
def star(func):
def inner(*args, **kwargs):
print("*" * 15)
func(*args, **kwargs)
print("*" * 15)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 15)
func(*args, **kwargs)
print("%" * 15)
return inner
@star
@percent
def printer(msg):
print(msg)
printer("Hello")
Natija
***************
%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%
***************
Yuqoridagi
@star
@percent
def printer(msg):
print(msg)
sintaksisi quyidagisiga teng
def printer(msg):
print(msg)
printer = star(percent(printer))
Dekoratorlarni qanday tartibda zanjirlashimiz muhim. Agar tartibni teskari qilsak, masalan,
@percent
@star
def printer(msg):
print(msg)
Natija bunday bo'lardi:
%%%%%%%%%%%%%%%
***************
Hello
***************
%%%%%%%%%%%%%%%
Ko'proq o'qing: Python @property decorator, Python property()