44 / 59

Python Decorators

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.

Dekoratorlarni o'rganish uchun zarur bilimlar

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,

example.py
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,

example.py
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

kod
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,

example.py
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.

Python dekoratorlari

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.

example.py
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()
  • funksiyani o'zining argumenti sifatida oladigan va 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.

example.py
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

kod
I got decorated
I am ordinary

Yuqorida ko'rsatilgan misolda, make_pretty() — dekorator. Kodga e'tibor bering,

example.py
decorated_func = make_pretty(ordinary)
  • Endi biz ordinary() funksiyasini make_pretty()ga argument sifatida uzatyapmiz.
  • make_pretty() funksiyasi ichki funksiyani qaytaradi, va u endi decorated_func o'zgaruvchisiga berilgan.
example.py
decorated_func()

Bu yerda, biz aslida inner() funksiyasini chaqiryapmiz, u yerda biz chop etyapmiz

Dekorator bilan @ belgisi

Funksiya chaqiruvini o'zgaruvchiga berish o'rniga, Python @ belgisi yordamida bu funksionallikka erishishning ancha nafisroq usulini taqdim etadi. Masalan,

example.py
def make_pretty(func):

    def inner():
        print("I got decorated")
        func()
    return inner

@make_pretty
def ordinary():
    print("I am ordinary")

ordinary()  

Natija

kod
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.

Parametrli funksiyalarni dekoratsiyalash

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:

example.py
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.

example.py
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

kod
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 dekoratorlarni zanjirlash (Chaining)

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.

example.py
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

kod
***************
%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%
***************

Yuqoridagi

example.py
@star
@percent
def printer(msg):
    print(msg)

sintaksisi quyidagisiga teng

example.py
def printer(msg):
    print(msg)
printer = star(percent(printer))

Dekoratorlarni qanday tartibda zanjirlashimiz muhim. Agar tartibni teskari qilsak, masalan,

example.py
@percent
@star
def printer(msg):
    print(msg)

Natija bunday bo'lardi:

kod
%%%%%%%%%%%%%%%
***************
Hello
***************
%%%%%%%%%%%%%%%

Ko'proq o'qing: Python @property decorator, Python property()

QBilimingizni sinab ko'ring
1 / 3

1. Dekorator nima?