Decorator trong Python là một công cụ mạnh mẽ. Nó giúp chúng ta tuỳ chỉnh logic của 1 function trong quá trình chạy.

Tuy nhiên, tự viết ra decorator để dùng sẽ không hề đơn giản cho người mới bắt đầu. Điều này đặc biệt đúng với những ai chưa quen với khái niệm First-class functions hoặc functional programming.

Về định nghĩa, decorator là 1 function nhận tham số là 1 function khác, giá trị trả về có thể là input function hoặc bất kì thứ gì khác.

Để minh hoạ cho khái niệm này, sau đây là ví dụ về 1 decorator biến đổi tham số của 1 function thành chữ in hoa:

def upperParams(func):
    def inner(*args, **kwargs):
        args = [arg.upper() for arg in args]
        return func(*args, **kwargs)
    return inner

@upperParams
def greeting(name):
    print('hello {}'.format(name))


greeting('world')
# kết quả: hello WORLD

Giải thích cho ví dụ trên:

@upperParams
def greeting(name):
    print('hello {}'.format(name))

Hoàn toàn tương đương với:

def greeting(name):
    print('hello {}'.format(name))
greeting = upperParams(greeting)

Như các diễn giải này:

upperParams là 1 function nhận input là 1 function, trong trường hợp này là greeting function.

Nó trả về hàm inner nhận bất kì tham số tuần tự hoặc tham số keyword nào.

Do đó, tham số được truyền vào sau cùng khi chạy greeting('world') chính xác được truyền vào inner function.

inner function convert tất cả tham số tuần tự thành chữ in hoa và đưa hết vào input function rồi return function này.

kết quả trả về là chuỗi hello WORLD như ta thấy.

Vậy có những trường hợp ta không cần upper thì sao? Có 2 cách:

  • Viết 2 decorator
  • Tham số hoá decorator để đặt điều kiện khi nào cần upper, khi nào không.

Ta có thể sửa decorator trên thành:

def upperParamsCondition(upper=True):
    def deco(func):
        def inner(*args, **kwargs):
            if upper is True:
                args = [arg.upper() for arg in args]
            return func(*args, **kwargs)
        return inner
    return deco

Chạy thử:

@upperParamsCondition()
def greeting2(name):
    print('hello {}'.format(name))

greeting2('world')
# kết quả: hello WORLD

@upperParamsCondition(False)
def greeting3(name):
    print('hello {}'.format(name))

greeting3('world')
# kết quả: hello world

Cắt nghĩa khi không dùng synstatic sugar:

@upperParamsCondition(False)
def greeting(name):
    print('hello {}'.format(name))

Tương đương với:

def greeting(name):
    print('hello {}'.format(name))
greeting = upperParamsCondition(False)(greeting)

upperParamsCondition thực chất là 1 decorator factory. Nó trả về 1 decorator (trong trường hợp này là deco function) để nhận input function sau khi nhận input của riêng nó. inner function sau đó có thể sử dụng tham số upper được pass vào trước đó để quyết định xem có convert in hoa hay không.

Đó là sự khác nhau cơ bản của decorator thường và decorator được tham số hoá.

  • Decorator thường sẽ nhận input function để xử lý.
  • Decorator được tham số hoá sẽ nhận tham số của riêng nó, trả về 1 decorator, decorator này mới thực sự nhận input function để xử lý.

DONE