DECORATOR FUNCTION Decorator function, một trong những khái niệm phức tạp nhất và hữu dụng nhất trong lập trình Python Nội dung Decorator trong Python là gì và tác dụng Cách định nghĩa c.
Trang 1DECORATOR FUNCTION
Decorator functon, m t trong nhưng khai ni m phưc tap nhât va hưu dung nhât ô ê trong l p trinh Python N i dung:â ô
• Decorator trong Python la gi va tac dung
• Cach định nghĩa cac decorator
• Ví du
• Viết code tốt hơn bằng decorator
Giới thiệu
1
2
3
@decorator
def functon_to_decorate():
pass
Tiền tố @ Cac decorator đặc biệt hưu ích trong việc giư code không bị lặp lại, va
chúng lam điều đó nhưng đồng thời cũng cải thiện khả năng đọc của code vi decorator chỉ la cac ham Python
01
02
03
04
05
06
07
08
09
def x_plus_2(x):
return x + 2
print(x_plus_2(2)) # 2 + 2 ==4
def x_squared(x):
return x * x
print(x_squared(3)) # 3 ^ 2 == 9
# Let's compose the two functons for x=2
Trang 211
12
13
14
15
16
print(x_squared(x_plus_2(2))) # (2 + 2) ^ 2 == 16
print(x_squared(x_plus_2(3))) # (3 + 2) ^ 2 == 25
print(x_squared(x_plus_2(4))) # (4 + 2) ^ 2 == 36
Tao ra một ham khac, ham x_plus_2_squared
1 x_squared(x_plus_2) # TypeError: unsupported operand type(s) for *:
'functon' and 'functon'
Không thể tổ hợp cac ham bằng cach nay bởi vi cả hai ham đều nhận cac con số như la cac đối số:
1
2
3
4
5
6
# Let's now create a proper functon compositon without actually applying the functon
x_plus_2_squared = lambda x: x_squared(x_plus_2(x))
print(x_plus_2_squared(2)) # (2 + 2) ^ 2 == 16
print(x_plus_2_squared(3)) # (3 + 2) ^ 2 == 25
print(x_plus_2_squared(4)) # (4 + 2) ^ 2 == 36
Định nghĩa lai cach ham x_squared lam việc:
1. Châp nhận một ham như một đối số
2. Trả về một ham khac
Đ t tên tên ham có thể tai tổ hợp củaă x_squared đơn giản la squared
Trang 32
3
4
5
6
def squared(func):
return lambda x: func(x) * func(x)
print(squared(x_plus_2)(2)) # (2 + 2) ^ 2 == 16
print(squared(x_plus_2)(3)) # (3 + 2) ^ 2 == 25
print(squared(x_plus_2)(4)) # (4 + 2) ^ 2 == 36
Bây giờ có thể sử dung nó với bât kỳ ham nao khac Dưới đây la một số ví du: 1
2
3
4
5
6
7
8
def x_plus_3(x):
return x + 3
def x_tmes_2(x):
return x * 2
print(squared(x_plus_3)(2)) # (2 + 3) ^ 2 == 25
print(squared(x_tmes_2)(2)) # (2 * 2) ^ 2 == 16
Có thể nói rằng ham squared decorate cac ham x_plus_2, x_plus_3 va x_tmes_2 1
2
x_plus_2 = squared(x_plus_2) # We decorated x_plus_2 with squared
print(x_plus_2(2)) # x_plus_2 now returns the decorated squared result: (2 + 2) ^ 2
01
02
03
def x_plus_2(x):
return x + 2
Trang 405
06
07
08
09
10
x_plus_2 = squared(x_plus_2)
# ^ This is completely equivalent with:
@squared
def x_plus_2(x):
return x + 2
Ký hiệu @ la một hinh thưc cú phap dễ đọc:
01
02
03
04
05
06
07
08
09
10
11
12
@squared
def x_tmes_3(x):
return 3 * x
print(x_tmes_3(2)) # (3 * 2) ^ 2 = 36
# It might be a bit confusing, but by decoratng it with squared, x_tmes_3 became in fact (3 * x) * (3 * x)
@squared
def x_minus_1(x):
return x - 1
print(x_minus_1(3)) # (3 - 1) ^ 2 = 4
Xây dựng Decorator
Trang 5Một decorator la một ham nhận một ham như la một đối số va trả về một ham khac, định nghĩa decorator:
1
2
3
def decorator(functon_to_decorate):
#
return decorated_functon
Có thể định nghĩa cac ham bên trong cac ham khac Trong hầu hết cac trường hợp, decorated_functon sẽ được định nghĩa bên trong decorator
1
2
3
4
def decorator(functon_to_decorate):
def decorated_functon(*args, **kwargs):
# Since we decorate `functon_to_decorate`, we should use it
somewhere inside here
return decorated_functon
01
02
03
04
05
06
07
08
09
10
11
import pytz
from datetme import datetme
def to_utc(functon_to_decorate):
def decorated_functon():
# Get the result of functon_to_decorate and transform the result to UTC return functon_to_decorate().astmezone(pytz.utc)
return decorated_functon
@to_utc
def package_pickup_tme():
Trang 613
14
15
16
17
18
19
20
21
22
23
""" This can come from a database or from an API """
tz = pytz.tmezone('US/Pacific')
return tz.localize(datetme(2017, 8, 2, 12, 30, 0, 0))
@to_utc
def package_delivery_tme():
""" This can come from a database or from an API """
tz = pytz.tmezone('US/Eastern')
return tz.localize(datetme(2017, 8, 2, 12, 30, 0, 0)) # What a coincidence, same tme different tmezone!
print("PICKUP: ", package_pickup_tme()) # '2017-08-02 19:30:00+00:00' print("DELIVERY: ", package_delivery_tme()) # '2017-08-02 16:30:00+00:00'
Ví dụ Thực tế
Một trường hợp sử dung rât phổ biến va rât kinh điển khac đối với decorator la lưu cache kết quả của một ham:
01
02
03
04
05
06
07
08
import tme
def cached(functon_to_decorate):
_cache = {} # Where we keep the results
def decorated_functon(*args):
start_tme = tme.tme()
print('_cache:', _cache)
if args not in _cache:
Trang 710
11
12
13
14
15
16
17
18
19
20
21
22
23
24
_cache[args] = functon_to_decorate(*args) # Perform the computaton and store it in cache
print('Compute tme: %ss' % round(tme.tme() - start_tme, 2))
return _cache[args]
return decorated_functon
@cached
def complex_computaton(x, y):
print('Processing ')
tme.sleep(2)
return x + y
print(complex_computaton(1, 2)) # 3, Performing the expensive operaton print(complex_computaton(1, 2)) # 3, SKIP performing the expensive
operaton
print(complex_computaton(4, 5)) # 9, Performing the expensive operaton print(complex_computaton(4, 5)) # 9, SKIP performing the expensive
operaton
print(complex_computaton(1, 2)) # 3, SKIP performing the expensive
operaton
Nếu ban nhin sơ qua code, ban có thể thây khó chịu Decorator không thể sử dung lai được! Nếu chúng ta decorate một ham khac (gọi
la another_complex_computaton) va gọi nó với cac tham số tương tự thi chúng ta
sẽ nhận được cac kết quả được cache từ ham complex_computaton Điều nay sẽ không xảy ra Decorator có thể sử dung lai va đây la lý do:
Trang 82
3
4
5
6
7
8
9
@cached
def another_complex_computaton(x, y):
print('Processing ')
tme.sleep(2)
return x * y
print(another_complex_computaton(1, 2)) # 2, Performing the expensive operaton
print(another_complex_computaton(1, 2)) # 2, SKIP performing the expensive operaton
print(another_complex_computaton(1, 2)) # 2, SKIP performing the expensive operaton
Ham chached được gọi một lần cho mỗi ham ma nó decorate, do đó, một
biến _cache khac được khởi tao mỗi lần va tồn tai trong ngư cảnh đó Hãy thử điều đó:
1
2
print(complex_computaton(10, 20)) # -> 30
print(another_complex_computaton(10, 20)) # -> 200
Decorator trong Thực tế
Decorator ma chúng ta vừa mớ viết, như ban có thể nhận thây, rât hưu ích Nó hưu ích đến nỗi một phiên bản phưc tap va manh mẽ hơn đã tồn tai trong mô
đun functools têu chuẩn Nó được đặt tên la lru_cache LRU la viết tắt của Least
Recently Used, một ky thu t cahe.â
01
02
03
from functools import lru_cache
@lru_cache()
Trang 905
06
07
08
09
10
11
12
13
def complex_computaton(x, y):
print('Processing ')
tme.sleep(2)
return x + y
print(complex_computaton(1, 2)) # Processing 3
print(complex_computaton(1, 2)) # 3
print(complex_computaton(2, 3)) # Processing 5
print(complex_computaton(1, 2)) # 3
print(complex_computaton(2, 3)) # 5
Một trong nhưng trường hợp sử dung decorator la trong framework Flask Nó thi gọn gang, đó la đoan code điều đầu tên ban nhin thây trên trang web Flask Đây
la đoan code đó:
01
02
03
04
05
06
07
08
09
10
from flask import Flask
app = Flask( name )
@app.route("/")
def hello():
return "Hello World!"
if name == " main ":
app.run()
Trang 10Decorator app.route gan ham hello lam trinh xử lý yêu cầu cho tuyến "/" Thật sự đơn giản
Một cach sử dung decorator gọn gang khac la bên trong Django Thông thường, cac ưng dung web có hai kiểu trang:
1. cac trang ban có thể xem ma không cần chưng thực (trang chủ, trang
landing, bai blog, đăng nhập, đăng ký)
2. cac trang ban cần phải được chưng thực để xem (cai đặt tểu sử, hộp thư đến, bảng điều khiển)
Nếu ban thử xem một kiểu trang thư hai, ban thường sẽ được chuyển hướng đến một trang đăng nhập Dưới đây la cach thực hiện điều đó trong Django:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
# Public Pages
def home(request):
return HttpResponse("<b>Home</b>")
def landing(request):
return HttpResponse("<b>Landing</b>")
# Authentcated Pages
@login_required(login_url='/login')
def dashboard(request):
return HttpResponse("<b>Dashboard</b>")
Trang 1118
19
20
@login_required(login_url='/login')
def profile_settings(request):
return HttpResponse("<b>Profile Settings</b>")
Quan sat xem cac chế độ view riêng tư gọn gang như thế nao được đanh dâu bằng login_required Khi xem qua code, nó rât rõ rang đối với người đọc ma cac trang yêu cầu người dùng đăng nhập va nhưng trang không yêu cầu
Phần tóm tắt
Tôi hy vọng ban đã có một buổi học thú vị về decorator bởi vi chúng đai diện cho một tính năng rât gọn gang trong Python Dưới đây la một số điều cần nhớ:
• Sử dung va thiết kế decorator đúng cach có thể lam cho code của ban trở nên tốt hơn, sach sẽ hơn va đẹp hơn
• Sử dung decorator có thể giúp ban DRY code—di chuyển cac code giống nhau từ bên trong cac ham vao decorator
• Khi ban sử dung cac decorator nhiều hơn, ban sẽ thây nhưng cach tốt hơn, phưc tap hơn để sử dung chúng