Trình trang trí đối tượng Python
Khi làm code, dù biết hay không, chúng ta vẫn thường bắt gặp decorator design pattern. Đây là một kỹ thuật lập trình để mở rộng chức năng của các lớp hoặc hàm mà không sửa đổi chúng. Mẫu thiết kế trang trí cho phép chúng tôi trộn và kết hợp các tiện ích mở rộng một cách dễ dàng. Python có cú pháp trang trí bắt nguồn từ mẫu thiết kế trang trí. Biết cách tạo và sử dụng trình trang trí có thể giúp bạn viết mã hiệu quả hơn Show
Trong bài đăng này, bạn sẽ khám phá mẫu trang trí và các bộ trang trí chức năng của Python Sau khi hoàn thành hướng dẫn này, bạn sẽ học
Bắt đầu dự án của bạn với cuốn sách mới Python for Machine Learning của tôi, bao gồm các hướng dẫn từng bước và các tệp mã nguồn Python cho tất cả các ví dụ Bắt đầu nàoGiới thiệu nhẹ nhàng về Trình trang trí trong Python Tổng quanHướng dẫn này được chia thành bốn phần
Mẫu trang trí là gì và tại sao nó hữu ích?Mẫu trang trí là một mẫu thiết kế phần mềm cho phép chúng ta tự động thêm chức năng vào các lớp mà không cần tạo các lớp con và ảnh hưởng đến hành vi của các đối tượng khác trong cùng một lớp. Bằng cách sử dụng mẫu trang trí, chúng tôi có thể dễ dàng tạo các hoán vị khác nhau của chức năng mà chúng tôi có thể muốn mà không cần tạo số lượng lớp con tăng theo cấp số nhân, làm cho mã của chúng tôi ngày càng phức tạp và cồng kềnh Trình trang trí thường được triển khai dưới dạng giao diện phụ của giao diện chính mà chúng tôi muốn triển khai và lưu trữ một đối tượng thuộc loại của giao diện chính. Sau đó, nó sẽ sửa đổi các phương thức mà nó muốn thêm chức năng nhất định bằng cách ghi đè các phương thức trong giao diện ban đầu và gọi các phương thức từ đối tượng được lưu trữ Sơ đồ lớp UML cho mẫu trang trí Trên đây là sơ đồ lớp UML cho mẫu thiết kế trang trí. Lớp trừu tượng trang trí chứa một đối tượng kiểu Tuy nhiên, với Python, chúng tôi có thể đơn giản hóa nhiều mẫu thiết kế này do gõ động cùng với các hàm và lớp là đối tượng hạng nhất. Mặc dù việc sửa đổi một lớp hoặc một hàm mà không thay đổi cách triển khai vẫn là ý tưởng chính của các trình trang trí, chúng ta sẽ khám phá cú pháp trình trang trí của Python trong phần sau Trình trang trí chức năng trong PythonTrình trang trí chức năng là một tính năng cực kỳ hữu ích trong Python. Nó được xây dựng dựa trên ý tưởng rằng các hàm và lớp là các đối tượng hạng nhất trong Python Hãy xem xét một ví dụ đơn giản, đó là gọi một hàm hai lần. Vì một hàm Python là một đối tượng và chúng ta có thể truyền một hàm làm đối số cho một hàm khác, nên tác vụ này có thể được thực hiện như sau 1 2 3 4 5 6 7 8 def repeat(fn): fn() fn()
def hello_world(). in("Xin chào thế giới. ")
repeat(hello_world) Một lần nữa, vì một hàm Python là một đối tượng, chúng ta có thể tạo một hàm để trả về một hàm khác, nghĩa là thực thi một hàm khác hai lần. Điều này được thực hiện như sau 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def repeat_decorator(fn): def decorated_fn(): fn() fn() # trả về một hàm return decorated_fn
def hello_world(). in ("Xin chào thế giới. ")
hello_world_twice = repeat_decorator(hello_world)
#gọi hàm hello_world_twice() Hàm được trả về bởi Ý tưởng về mẫu trang trí áp dụng ở đây. Nhưng chúng ta không cần định nghĩa rõ ràng giao diện và các lớp con. Trên thực tế, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def repeat_decorator(fn): def decorated_fn(): fn() fn() # trả về một hàm return decorated_fn
def hello_world(). in ("Xin chào thế giới. ")
hello_world = repeat_decorator(hello_world)
#gọi hàm hello_world() Tức là, thay vì gán hàm mới tạo cho Thật vậy, đoạn mã trên có chức năng tương đương như sau 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # chức năng trang trí gọi chức năng hai lần def repeat_decorator(fn): def decorated_fn(): fn() fn() # trả về một hàm trả lại được trang trí_ fn
# sử dụng decorator trên hàm hello_world @repeat_decorator def hello_world(). in ("Xin chào thế giới. ")
#gọi hàm hello_world() Trong đoạn mã trên, Ghi chú. Cú pháp Chúng ta cũng có thể triển khai các trình trang trí nhận các đối số, nhưng điều này sẽ phức tạp hơn một chút vì chúng ta cần có thêm một lớp lồng nhau. Nếu chúng ta mở rộng ví dụ trên để xác định số lần lặp lại lời gọi hàm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def repeat_decorator(num_repeats = 2): # repeat_decorator sẽ trả về một hàm là trình trang trí def inner_decorator(fn): def decorated_fn(): cho i trong phạm vi(num_repeats): fn() # trả lại hàm mới trả lại được trang trí_ fn # trả về trình trang trí thực sự lấy chức năng làm đầu vào return inner_ trang trí
# sử dụng trình trang trí với đối số num_repeat được đặt là 5 để lặp lại lời gọi hàm 5 lần @repeat_decorator(5) def hello_world(). in("Xin chào thế giới. ")
#gọi hàm hello_world()
1 2 3 4 5 Xin chào thế giới. Xin chào thế giới. Xin chào thế giới. Xin chào thế giới. Xin chào thế giới. Trước khi kết thúc phần này, chúng ta nên nhớ rằng các trình trang trí cũng có thể được áp dụng cho các lớp ngoài các hàm. Vì lớp trong Python cũng là một đối tượng, nên chúng ta có thể định nghĩa lại một lớp theo cách tương tự Bạn muốn bắt đầu với Python cho Machine Learning?Tham gia khóa học xử lý sự cố email miễn phí trong 7 ngày của tôi ngay bây giờ (có mã mẫu) Nhấp để đăng ký và cũng nhận được phiên bản PDF Ebook miễn phí của khóa học Tải xuống khóa học nhỏ MIỄN PHÍ của bạn Các trường hợp sử dụng của trang tríCú pháp trang trí trong Python giúp việc sử dụng trang trí dễ dàng hơn. Có nhiều lý do chúng ta có thể sử dụng một trang trí. Một trong những trường hợp sử dụng phổ biến nhất là chuyển đổi dữ liệu ngầm. Ví dụ: chúng ta có thể định nghĩa một hàm giả định rằng tất cả các hoạt động đều dựa trên các mảng có nhiều mảng và sau đó tạo một trình trang trí để đảm bảo điều đó xảy ra bằng cách sửa đổi đầu vào 1 2 3 4 5 6 7 8 # chức năng trang trí để đảm bảo đầu vào gọn gàng def ensure_numpy(fn): def decorated_function(dữ liệu): # chuyển đổi đầu vào thành mảng có nhiều mảng mảng = np. sắp xếp(dữ liệu) # gọi fn trên mảng numpy đầu vào return fn(mảng) return decorated_function Chúng ta có thể thêm vào trình trang trí của mình bằng cách sửa đổi đầu ra của hàm, chẳng hạn như làm tròn các giá trị dấu phẩy động 1 2 3 4 5 6 7 8 # chức năng trang trí để đảm bảo đầu vào gọn gàng # và làm tròn đầu ra đến 4 chữ số thập phân def ensure_numpy(fn): def decorated_function(dữ liệu): mảng = np. sắp xếp(dữ liệu) đầu ra = fn(array) return np. xung quanh(đầu ra, 4) return decorated_function Hãy xem xét ví dụ tìm tổng của một mảng. Một mảng numpy có tích hợp sẵn 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 nhập numpy as np nhập gấu trúc as pd
# chức năng trang trí để đảm bảo đầu vào gọn gàng # và làm tròn đầu ra đến 4 chữ số thập phân def ensure_numpy(fn): def decorated_function(dữ liệu): mảng = np. sắp xếp(dữ liệu) đầu ra = fn(array) return np. xung quanh(đầu ra, 4) trả về được trang trí_ chức năng
@ensure_numpy def numpysum(mảng): trả về mảng. tổng()
x = np. ngẫu nhiên. randn(10,3) y = pd. Khung dữ liệu(x, cột=["A", "B", "C"])
# đầu ra của numpy. hàm tổng () in("x. Tổng(). ", x. tổng()) in()
# đầu ra của gấu trúc. hàm tổng () in("y. Tổng(). ", y. tổng()) in(y. tổng()) in()
# gọi hàm numpysum được trang trí in("numpysum(x). ", numpysum(x)) in("numpysum(y). ", numpysum(y)) Chạy đoạn mã trên cho chúng ta đầu ra 1 2 3 4 5 6 7 8 9 10 11 12 13 x. tổng(). 0. 3948331694737762
y. tổng(). A -1. 175484 B 2. 496056 C -0. 925739 dtype. float64 A -1. 175484 B 2. 496056 C -0. 925739 dtype. float64
numpysum(x). 0. 3948 numpysum(y). 0. 3948 Đây là một ví dụ đơn giản. Nhưng hãy tưởng tượng nếu chúng ta định nghĩa một hàm mới tính độ lệch chuẩn của các phần tử trong một mảng. Chúng ta chỉ cần sử dụng cùng một trình trang trí và sau đó hàm cũng sẽ chấp nhận DataFrame của gấu trúc. Do đó, tất cả mã để đánh bóng đầu vào được lấy ra khỏi các chức năng này bằng cách gửi chúng vào trình trang trí. Đây là cách chúng ta có thể sử dụng lại mã một cách hiệu quả Một số ví dụ thực tế về DecoratorsBây giờ chúng ta đã học cú pháp decorator trong Python, hãy xem chúng ta có thể làm gì với nó ghi nhớCó một số lệnh gọi hàm mà chúng ta thực hiện lặp đi lặp lại, nhưng ở đó các giá trị hiếm khi thay đổi, nếu có. Đây có thể là lệnh gọi đến máy chủ nơi dữ liệu tương đối tĩnh hoặc là một phần của thuật toán lập trình động hoặc hàm toán học chuyên sâu về tính toán. Chúng tôi có thể muốn ghi nhớ các cuộc gọi chức năng này, tôi. e. , lưu trữ giá trị đầu ra của chúng trên sổ ghi nhớ ảo để sử dụng lại sau này Trình trang trí là cách tốt nhất để thực hiện chức năng ghi nhớ. Chúng ta chỉ cần nhớ đầu vào và đầu ra của hàm nhưng giữ nguyên hành vi của hàm. Dưới đây là một ví dụ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 nhập dưa chua nhập hashlib
MEMO = {} # Để ghi nhớ chức năng
def ghi nhớ(fn): def _deco(*args, **kwargs): # chọn các đối số của hàm và lấy hàm băm làm khóa lưu trữ phím = (fn.__name__, hashlib. md5(dưa chua. kết xuất((args, . kwargs), 4)).hexdigest()) # kiểm tra xem khóa có tồn tại không nếu phím trong MEMO: ret = dưa chua. tải(MEMO[phím]) khác. ret = fn(*args, **kwargs) MEMO[phím] = pickle.kết xuất(rút lui) trả lại rút lại return _deco
@ghi nhớ def fibonacci(n): nếu n trong [0, 1]: trả lại n khác. return fibonacci(n-1) + fibonacci(n-2)
in(fibonacci(40)) in(MEMO) Trong ví dụ này, chúng tôi đã triển khai Chúng tôi sử dụng Ví dụ trên sử dụng 1 2 3 4 5 6 7 8 102334155 {('fibonacci', '635f1664f168e2a15b8e43f20d45154b'). b'\x80\x04K\x01. ', ('fibonacci', 'd238998870ae18a399d03477dad0c0a8'). b'\x80\x04K\x00. ', ('fibonacci', 'dbed6abf8fcf4beec7fc97f3170de3cc'). b'\x80\x04K\x01. ', ... ('fibonacci', 'b9954ff996a4cd0e36fffb09f982b08e'). b'\x80\x04\x95\x06\x00\x00\x00\x00\x00\x00\x00J)pT\x02. ', ('fibonacci', '8c7aba62def8063cf5afe85f42372f0d'). b'\x80\x04\x95\x06\x00\x00\x00\x00\x00\x00\x00J\xa2\x0e\xc5\x03. ', ('fibonacci', '6de8535f23d756de26959b4d6e1f66f6'). b'\x80\x04\x95\x06\x00\x00\x00\x00\x00\x00\x00J\xcb~\x19\x06. '} Bạn có thể thử xóa dòng Ghi nhớ rất hữu ích cho các chức năng đắt tiền mà kết quả đầu ra không thay đổi thường xuyên, chẳng hạn như chức năng sau đây đọc một số dữ liệu thị trường chứng khoán từ Internet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .. .
nhập pandas_datareader as pdr
@ghi nhớ def get_stock_data(ticker): # lấy dữ liệu từ stooq df = pdr. stooq. StooqDailyReader(ký hiệu=ticker . , start="1/1/00", end="31/12/21").đọc() return df
#kiểm tra lệnh gọi chức năng nhập CHồ sơ dưới dạng hồ sơ nhập pstats
cho i trong phạm vi(1, 3): print(f"Run {i}") run_profile = profile. Hồ sơ() run_profile. bật() get_stock_data("^DJI") run_profile. vô hiệu hóa() pstats. Số liệu thống kê(run_profile). print_stats(0) Nếu được triển khai chính xác, cuộc gọi tới 1 2 3 4 5 Chạy 1 17492 lệnh gọi hàm (17051 lệnh gọi nguyên thủy) trong 1. 452 giây
Chạy 2 221 lệnh gọi hàm (218 lệnh gọi nguyên thủy) trong 0. 001 giây Điều này đặc biệt hữu ích nếu bạn đang làm việc trên sổ ghi chép Jupyter. Nếu bạn cần tải xuống một số dữ liệu, hãy bọc nó trong trình trang trí ghi nhớ. Vì việc phát triển một dự án máy học có nghĩa là nhiều lần thay đổi mã của bạn để xem kết quả có tốt hơn không, chức năng tải xuống được ghi nhớ giúp bạn tiết kiệm rất nhiều thời gian chờ đợi không cần thiết Bạn có thể tạo một trình trang trí ghi nhớ mạnh mẽ hơn bằng cách lưu dữ liệu vào cơ sở dữ liệu (e. g. , kho lưu trữ khóa-giá trị như GNU dbm hoặc cơ sở dữ liệu trong bộ nhớ như memcached hoặc Redis). Nhưng nếu bạn chỉ cần chức năng như trên thì Python 3. 2 hoặc sau đó đã gửi cho bạn công cụ trang trí 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 nhập công cụ chức năng
nhập pandas_datareader as pdr
# ghi nhớ bằng lru_cache @công cụ chức năng. lru_cache def get_stock_data(ticker): # lấy dữ liệu từ stooq df = pdr. stooq. StooqDailyReader(ký hiệu=ticker . , start="1/1/00", end="31/12/21").đọc() return df
# thử nghiệm lệnh gọi chức năng nhập CHồ sơ dưới dạng hồ sơ nhập pstats
cho i trong phạm vi(1, 3): print(f"Run {i}") run_profile = profile. Hồ sơ() run_profile. bật() get_stock_data("^DJI") run_profile. vô hiệu hóa() pstats. Số liệu thống kê(run_profile). print_stats(0) Ghi chú. Danh mục chức năngMột ví dụ khác mà chúng ta có thể muốn xem xét việc sử dụng các bộ trang trí chức năng là để đăng ký các chức năng trong một danh mục. Nó cho phép chúng ta liên kết các hàm với một chuỗi và truyền các chuỗi này làm đối số cho các hàm khác. Đây là bước khởi đầu của việc tạo ra một hệ thống cho phép các trình cắm thêm do người dùng cung cấp. Hãy minh họa điều này bằng một ví dụ. Dưới đây là một decorator và một hàm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # kích hoạt. py
KÍCH HOẠT = {}
def đăng ký(tên): def trang trí(fn): # gán fn cho khóa "tên" trong KÍCH HOẠT KÍCH HOẠT[tên] = fn # return fn chưa sửa đổi trả lại fn return trang trí
def kích hoạt(x, kind): thử. fn = KÍCH HOẠT[kind] return fn(x) ngoại trừ Lỗi phím. in("Chức năng kích hoạt %s chưa xác định" % kind) Sau khi xác định trình trang trí 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # chức năng. py
từ kích hoạt nhập đăng ký nhập numpy as np
@đăng ký("relu") def relu(x): return np. ở đâu(x>0, x, 0)
@đăng ký("sigmoid") def sigm(x): return 1/(1+np.exp(-x))
@đăng ký("tanh") def tánh(x): return np. tánh(x) Chúng tôi đã đăng ký các hàm “relu”, “sigmoid” và “tanh” cho các chuỗi tương ứng của chúng bằng cách xây dựng liên kết này trong từ điển Bây giờ, hãy xem cách chúng ta có thể sử dụng các chức năng mới đăng ký của mình 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 nhập numpy as np từ kích hoạt nhập kích hoạt
# tạo ma trận ngẫu nhiên x = np. ngẫu nhiên. randn(5,3) in(x)
# thử kích hoạt ReLU trên ma trận relu_x = kích hoạt(x, "relu") in(relu_x)
# tải các chức năng và gọi lại kích hoạt ReLU nhập chức năng relu_x = kích hoạt(x, "relu") in(relu_x) cung cấp cho chúng tôi đầu ra 1 2 3 4 5 6 7 8 9 10 11 12 [[-0. 81549502 -0. 81352867 1. 41539545] [-0. 28782853 -1. 59323543 -0. 19824959] [ 0. 06724466 -0. 26622761 -0. 41893662] [ 0. 47927331 -1. 84055276 -0. 23147207] [-0. 18005588 -1. 20837815 -1. 34768876]] Chức năng kích hoạt relu không xác định Không có [[0. 0. 1. 41539545] [0. 0. 0. ] [0. 06724466 0. 0. ] [0. 47927331 0. 0. ] [0. 0. 0. ]] Quan sát rằng trước khi chúng tôi đến dòng Lưu ý rằng chúng tôi chưa bao giờ gọi bất kỳ thứ gì trong mô-đun Nếu bạn đã quen thuộc với Keras, bạn nên cộng hưởng những điều trên với cú pháp sau 1 2 3 4 5 lớp = máy ảnh. lớp. Mật độ(128, kích hoạt="relu")
mô hình. biên dịch(mất mát=", trình tối ưu hóa="adam", số liệu=["sparse_categorical_accuracy"]) Máy ảnh đã xác định hầu hết tất cả các thành phần bằng cách sử dụng trình trang trí có tính chất tương tự. Do đó chúng ta có thể tham khảo các khối xây dựng theo tên. Nếu không có cơ chế này, chúng ta phải sử dụng cú pháp sau mọi lúc, điều này khiến chúng ta phải nhớ vị trí của rất nhiều thành phần 1 2 3 4 5 lớp = máy ảnh. lớp. Mật độ(128, kích hoạt . =keras.kích hoạt. relu)
mô hình. biên dịch(mất=máy ảnh . .tổn thất. SparseCategoricalCrossentropy(), trình tối ưu hóa=máy ảnh. trình tối ưu hóa. Adam(), số liệu=[máy ảnh.số liệu. Độ chính xác phân loại thưa thớt()]) đọc thêmPhần này cung cấp nhiều tài nguyên hơn về chủ đề này nếu bạn muốn tìm hiểu sâu hơn Bài viết
Sách
API
Tóm lượcTrong bài đăng này, bạn đã khám phá mẫu thiết kế trang trí và cú pháp trang trí của Python. Bạn cũng đã thấy một số trường hợp sử dụng cụ thể của trình trang trí có thể giúp chương trình Python của bạn chạy nhanh hơn hoặc dễ mở rộng hơn Trình trang trí trong Python là gì?Trình trang trí là mẫu thiết kế trong Python cho phép người dùng thêm chức năng mới vào đối tượng hiện có mà không sửa đổi cấu trúc của đối tượng . Trình trang trí thường được gọi trước khi định nghĩa chức năng bạn muốn trang trí.
Trình trang trí lớp Python là gì?Trình trang trí lớp Python là gì? . Ví dụ, một hàm có thể được trang trí bằng một lớp có thể chấp nhận đối số hoặc với một lớp không thể chấp nhận đối số. adds a class to a function, and it can be achieved without modifying the source code. For example, a function can be decorated with a class that can accept arguments or with a class that can accept no arguments.
Các loại trang trí khác nhau trong Python là gì?Trên thực tế, có hai loại trình trang trí trong Python — trình trang trí lớp và trình trang trí chức năng — nhưng tôi sẽ tập trung vào trình trang trí chức năng ở đây.
Trang trí trong OOP là gì?Trong lập trình hướng đối tượng, mẫu trang trí là mẫu thiết kế cho phép hành vi được thêm vào một đối tượng riêng lẻ một cách linh hoạt mà không ảnh hưởng đến hành vi của các đối tượng khác từ cùng một đối tượng . . |