Trình trang trí Python cho phép bạn thay đổi hành vi của một hàm mà không cần sửa đổi chính hàm đó.
Trong bài viết này, tôi sẽ chỉ cho bạn cách tạo và sử dụng decorator. Bạn sẽ thấy việc sử dụng tính năng Python nâng cao này dễ dàng như thế nào.
Trong bài viết này tôi sẽ thảo luận về các chủ đề sau:
- Khi nào nên sử dụng trình trang trí trong Python
- Các khối xây dựng bạn sử dụng để tạo trang trí
- Cách tạo trình trang trí Python
- Các ví dụ thực tế về trình trang trí Python
- Trình trang trí lớp trong Python
Khi nào nên sử dụng Trình trang trí trong Python
Bạn sẽ sử dụng một trình trang trí khi bạn cần thay đổi hành vi của một chức năng mà không sửa đổi chính chức năng đó. Một vài ví dụ điển hình là khi bạn muốn thêm ghi nhật ký, kiểm tra hiệu suất, thực hiện lưu vào bộ nhớ đệm, xác minh quyền, v.v.
Bạn cũng có thể sử dụng một mã khi cần chạy cùng một mã trên nhiều chức năng. Điều này tránh cho bạn viết mã trùng lặp.
Dưới đây là các khối xây dựng được sử dụng để tạo trình trang trí Python
Để hiểu rõ hơn về cách hoạt động của decorator, trước tiên bạn nên hiểu một vài khái niệm.
- Một chức năng là một đối tượng. Do đó, một chức năng có thể được gán cho một biến. Hàm có thể được truy cập từ biến đó.
def my_function():
print('I am a function.')
# Assign the function to a variable without parenthesis. We don't want to execute the function.
description = my_function
# Accessing the function from the variable I assigned it to.
print(description())
# Output
'I am a function.'
2. Một chức năng có thể được lồng trong một chức năng khác.
def outer_function():
def inner_function():
print('I came from the inner function.')
# Executing the inner function inside the outer function.
inner_function()
outer_function()
# Output
I came from the inner function.
Lưu ý rằng inner_function
không có sẵn bên ngoài outer_function
. Nếu tôi cố gắng thực hiện inner_function
bên ngoài của outer_function
Tôi nhận được một ngoại lệ NameError.
inner_function()
Traceback (most recent call last):
File "/tmp/my_script.py", line 9, in <module>
inner_function()
NameError: name 'inner_function' is not defined
3. Vì một hàm có thể được lồng bên trong một hàm khác nên nó cũng có thể được trả về.
def outer_function():
'''Assign task to student'''
task = 'Read Python book chapter 3.'
def inner_function():
print(task)
return inner_function
homework = outer_function()
homework()
# Output
'Read Python book chapter 5.'
4. Một hàm có thể được truyền cho một hàm khác dưới dạng đối số.
def friendly_reminder(func):
'''Reminder for husband'''
func()
print('Don\'t forget to bring your wallet!')
def action():
print('I am going to the store buy you something nice.')
# Calling the friendly_reminder function with the action function used as an argument.
friendly_reminder(action)
# Output
I am going to the store buy you something nice.
Don't forget to bring your wallet!
Cách tạo Trình trang trí Python
Để tạo một hàm trang trí trong Python, tôi tạo một hàm bên ngoài lấy một hàm làm đối số. Ngoài ra còn có chức năng bên trong bao bọc xung quanh chức năng được trang trí.
Đây là cú pháp cho một trình trang trí Python cơ bản:
def my_decorator_func(func):
def wrapper_func():
# Do something before the function.
func()
# Do something after the function.
return wrapper_func
Để sử dụng một trình trang trí, bạn gắn nó vào một chức năng như bạn thấy trong mã bên dưới. Chúng tôi sử dụng một trình trang trí bằng cách đặt tên của trình trang trí ngay phía trên chức năng mà chúng tôi muốn sử dụng nó. Bạn đặt trước chức năng trang trí bằng một @
Biểu tượng.
@my_decorator_func
def my_func():
pass
Đây là một ví dụ đơn giản. Trình trang trí này ghi lại ngày và giờ một chức năng được thực thi:
from datetime import datetime
def log_datetime(func):
'''Log the date and time of a function'''
def wrapper():
print(f'Function: {func.__name__}\nRun on: {datetime.today().strftime("%Y-%m-%d %H:%M:%S")}')
print(f'{"-"*30}')
func()
return wrapper
@log_datetime
def daily_backup():
print('Daily backup job has finished.')
daily_backup()
# Output
Daily backup job has finished.
Function: daily_backup
Run on: 2021-06-06 06:54:14
---------------------------
Cách thêm đối số vào trang trí trong Python
Người trang trí có thể có các đối số được truyền cho họ. Để thêm đối số vào trình trang trí, tôi thêm *args
và **kwargs
đến các chức năng bên trong.
*args
sẽ nhận một số lượng không giới hạn các đối số thuộc bất kỳ loại nào, chẳng hạn như10
,True
hoặc'Brandon'
.**kwargs
sẽ có số lượng đối số từ khóa không giới hạn, chẳng hạn nhưcount=99
,is_authenticated=True
hoặcname="Brandon"
.
Đây là một trang trí với các đối số:
def my_decorator_func(func):
def wrapper_func(*args, **kwargs):
# Do something before the function.
func(*args, **kwargs)
# Do something after the function.
return wrapper_func
@my_decorator_func
def my_func(my_arg):
'''Example docstring for function'''
pass
Người trang trí ẩn chức năng mà họ đang trang trí. Nếu tôi kiểm tra __name__
hoặc __doc__
phương pháp chúng tôi nhận được một kết quả bất ngờ.
print(my_func.__name__)
print(my_func.__doc__)
# Output
wrapper_func
None
Để khắc phục vấn đề này, tôi sẽ sử dụng functools
. Funcools kết thúc tốt đẹp sẽ cập nhật trình trang trí với các thuộc tính chức năng được trang trí.
from functools import wraps
def my_decorator_func(func):
@wraps(func)
def wrapper_func(*args, **kwargs):
func(*args, **kwargs)
return wrapper_func
@my_decorator_func
def my_func(my_args):
'''Example docstring for function'''
pass
Bây giờ tôi nhận được đầu ra mà tôi đang mong đợi.
print(my_func.__name__)
print(my_func.__doc__)
# Output
my_func
Example docstring for function
Ví dụ về Trình trang trí Python đang hoạt động
Tôi đã tạo một công cụ trang trí sẽ đo bộ nhớ và tốc độ của một chức năng.
Chúng tôi sẽ sử dụng trình trang trí để kiểm tra việc tạo danh sách hiệu suất bằng bốn phương pháp: phạm vi, mức độ hiểu danh sách, nối thêm và nối.
from functools import wraps
import tracemalloc
from time import perf_counter
def measure_performance(func):
'''Measure performance of a function'''
@wraps(func)
def wrapper(*args, **kwargs):
tracemalloc.start()
start_time = perf_counter()
func(*args, **kwargs)
current, peak = tracemalloc.get_traced_memory()
finish_time = perf_counter()
print(f'Function: {func.__name__}')
print(f'Method: {func.__doc__}')
print(f'Memory usage:\t\t {current / 10**6:.6f} MB \n'
f'Peak memory usage:\t {peak / 10**6:.6f} MB ')
print(f'Time elapsed is seconds: {finish_time - start_time:.6f}')
print(f'{"-"*40}')
tracemalloc.stop()
return wrapper
@measure_performance
def make_list1():
'''Range'''
my_list = list(range(100000))
@measure_performance
def make_list2():
'''List comprehension'''
my_list = [l for l in range(100000)]
@measure_performance
def make_list3():
'''Append'''
my_list = []
for item in range(100000):
my_list.append(item)
@measure_performance
def make_list4():
'''Concatenation'''
my_list = []
for item in range(100000):
my_list = my_list + [item]
print(make_list1())
print(make_list2())
print(make_list3())
print(make_list4())
# Output
Function: make_list1
Method: Range
Memory usage: 0.000072 MB
Peak memory usage: 3.693040 MB
Time elapsed is seconds: 0.049359
----------------------------------------
Function: make_list2
Method: List comprehension
Memory usage: 0.000856 MB
Peak memory usage: 3.618244 MB
Time elapsed is seconds: 0.052338
----------------------------------------
Function: make_list3
Method: Append
Memory usage: 0.000448 MB
Peak memory usage: 3.617692 MB
Time elapsed is seconds: 0.060719
----------------------------------------
Function: make_list4
Method: Concatenation
Memory usage: 0.000440 MB
Peak memory usage: 4.393292 MB
Time elapsed is seconds: 61.649138
----------------------------------------
Bạn cũng có thể sử dụng các công cụ trang trí với các lớp. Hãy xem cách bạn sử dụng các công cụ trang trí với một lớp Python.
Trong ví dụ này, lưu ý rằng không có @
nhân vật tham gia. với __call__
phương thức trang trí được thực thi khi một thể hiện của lớp được tạo.
Lớp này theo dõi số lần một chức năng để truy vấn API đã được chạy. Khi nó đạt đến giới hạn, trình trang trí sẽ dừng chức năng thực thi.
import requests
class LimitQuery:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.limit = args[0]
if self.count < self.limit:
self.count += 1
return self.func(*args, **kwargs)
else:
print(f'No queries left. All {self.count} queries used.')
return
@LimitQuery
def get_coin_price(limit):
'''View the Bitcoin Price Index (BPI)'''
url = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
if url.status_code == 200:
text = url.json()
return f"${float(text['bpi']['USD']['rate_float']):.2f}"
print(get_coin_price(5))
print(get_coin_price(5))
print(get_coin_price(5))
print(get_coin_price(5))
print(get_coin_price(5))
print(get_coin_price(5))
# Output
$35968.25
$35896.55
$34368.14
$35962.27
$34058.26
No queries left. All 5 queries used.
Lớp này sẽ theo dõi trạng thái của lớp.
Trong bài viết này, tôi đã nói về cách truyền một hàm cho một biến, các hàm lồng nhau, trả về hàm và truyền một hàm cho một hàm khác làm đối số.
Tôi cũng đã chỉ cho bạn cách tạo và sử dụng trình trang trí Python cùng với một số ví dụ thực tế. Bây giờ tôi hy vọng rằng bạn sẽ có thể thêm các công cụ trang trí vào các dự án của mình.
Theo dõi tôi trên Github | DEV.to