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

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

  • Mẫu trang trí là gì và tại sao nó hữu ích
  • Trình trang trí chức năng của Python và cách sử dụng chúng

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ào

Trình trang trí đối tượng Python

Giới thiệu nhẹ nhàng về Trình trang trí trong Python
Ảnh của Olya Kobruseva. Một số quyền được bảo lưu

Tổng quan

Hướ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?
  • Trình trang trí chức năng trong Python
  • Các trường hợp sử dụng của trang trí
  • Một số ví dụ thực tế về decorator

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ữ

Trình trang trí đối tượng Python

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 OriginalInterface; . Để khởi tạo DecoratorClass cụ thể của chúng tôi, chúng tôi sẽ cần chuyển vào một lớp cụ thể triển khai OriginalInterface, và sau đó khi chúng tôi thực hiện các cuộc gọi phương thức tới DecoratorClass.method1(), thì DecoratorClass của chúng tôi sẽ sửa đổi đầu ra từ đối tượng method1()

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 Python

Trì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 repeat_decorator() ở trên được tạo khi nó được gọi, vì nó phụ thuộc vào đối số được cung cấp. Ở phần trên, chúng ta đã chuyển hàm hello_world làm đối số cho hàm repeat_decorator() và nó trả về hàm decorated_fn, được gán cho DecoratorClass0. Sau đó, chúng ta có thể gọi DecoratorClass1 vì bây giờ nó là một hàm

Ý 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ế, hello_world là một tên được định nghĩa như một hàm trong ví dụ trên. Không có gì ngăn cản chúng ta định nghĩa lại tên này thành tên khác. Do đó chúng ta cũng có thể làm 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 = repeat_decorator(hello_world)

 

#gọi hàm

hello_world()

Tức là, thay vì gán hàm mới tạo cho DecoratorClass0, chúng ta ghi đè lên hello_world thay thế. Trong khi tên hello_world được gán lại cho một chức năng khác, chức năng trước đó vẫn tồn tại nhưng không được hiển thị cho chúng tôi

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, DecoratorClass6 trước định nghĩa hàm có nghĩa là chuyển hàm vào repeat_decorator() và gán lại tên của nó cho đầu ra. Đó là, có nghĩa là DecoratorClass8. Dòng DecoratorClass9 là cú pháp trang trí trong Python

Ghi chú. Cú pháp DecoratorClass9 cũng được sử dụng trong Java nhưng có ý nghĩa khác khi đó là một chú thích về cơ bản là siêu dữ liệu chứ không phải là một công cụ trang trí

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()

repeat_decorator() nhận một đối số và trả về một hàm là công cụ trang trí thực tế cho hàm hello_world (i. e. , gọi OriginalInterface,3 trả về OriginalInterface,4 với biến cục bộ OriginalInterface,5 được đặt). Đoạn mã trên sẽ in như sau

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 OriginalInterface,6, pandas DataFrame cũng vậy. Nhưng cái sau là tính tổng trên các cột chứ không phải tính tổng trên tất cả các phần tử. Do đó, một mảng có nhiều mảng sẽ tính tổng bằng một giá trị dấu phẩy động trong khi DataFrame sẽ tính tổng bằng một vectơ giá trị. Nhưng với trình trang trí ở trên, chúng ta có thể viết một hàm cung cấp cho bạn đầu ra nhất quán trong cả hai trường hợp

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ề Decorators

Bâ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 OriginalInterface,7 để làm việc với từ điển toàn cầu OriginalInterface,8 sao cho tên của hàm cùng với các đối số trở thành khóa và kết quả trả về của hàm trở thành giá trị. Khi hàm được gọi, trình trang trí sẽ kiểm tra xem khóa tương ứng có tồn tại trong OriginalInterface,8 hay không và giá trị được lưu trữ sẽ được trả về. Mặt khác, chức năng thực tế được gọi và giá trị trả về của nó được thêm vào từ điển

Chúng tôi sử dụng DecoratorClass.method1()0 để tuần tự hóa đầu vào và đầu ra và sử dụng DecoratorClass.method1()1 để tạo hàm băm của đầu vào vì không phải mọi thứ đều có thể là chìa khóa cho từ điển Python (e. g. , DecoratorClass.method1()2 là loại không thể băm; . Tuần tự hóa bất kỳ cấu trúc tùy ý nào thành một chuỗi có thể khắc phục điều này và đảm bảo rằng dữ liệu trả về là bất biến. Hơn nữa, việc băm đối số hàm sẽ tránh lưu trữ một khóa dài đặc biệt trong từ điển (ví dụ: khi chúng ta chuyển một mảng lớn có nhiều mảng cho hàm)

Ví dụ trên sử dụng DecoratorClass.method1()3 để chứng minh sức mạnh của việc ghi nhớ. Gọi DecoratorClass.method1()4 sẽ tạo ra số Fibonacci thứ n. Chạy ví dụ trên sẽ tạo ra kết quả sau, trong đó chúng ta có thể thấy số Fibonacci thứ 40 là 102334155 và cách từ điển OriginalInterface,8 được sử dụng để lưu trữ các lệnh gọi khác nhau đến hàm

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 DecoratorClass.method1()6 trong đoạn mã trên. Bạn sẽ thấy chương trình mất nhiều thời gian hơn để chạy (vì mỗi lệnh gọi hàm gọi thêm hai lệnh gọi hàm nữa; do đó chương trình đang chạy trong O(2^n) thay vì O(n) như trong trường hợp được ghi nhớ), hoặc thậm chí bạn có thể

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 DecoratorClass.method1()7 sẽ đắt hơn lần đầu tiên và ít tốn kém hơn sau đó. Đầu ra từ đoạn mã trên cung cấp cho chúng 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í DecoratorClass.method1()8 từ thư viện tích hợp sẵn DecoratorClass.method1()9, vì vậy bạn không cần phải viết công cụ trang trí của riêng mình

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ú. DecoratorClass.method1()8 triển khai bộ nhớ đệm LRU, giới hạn kích thước của nó đối với các lệnh gọi gần đây nhất (mặc định là 128) đối với chức năng. Trong Trăn 3. 9, cũng có một DecoratorClass1, không giới hạn về kích thước mà không cần thanh lọc LRU

Danh mục chức năng

Mộ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 DecoratorClass2 mà chúng ta sẽ sử dụng sau. Giả sử đoạn mã sau được lưu trong tệp DecoratorClass3

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í DecoratorClass4 trong đoạn mã trên, bây giờ chúng ta có thể sử dụng nó để đăng ký các hàm và liên kết các chuỗi với chúng. Hãy có tệp DecoratorClass5 như vậy

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 DecoratorClass6

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 DecoratorClass7, kích hoạt ReLU không tồn tại. Do đó gọi hàm sẽ in thông báo lỗi và kết quả là DecoratorClass8. Sau đó, sau khi chúng tôi chạy dòng DecoratorClass9 đó, chúng tôi sẽ tải các chức năng được định nghĩa giống như mô-đun trình cắm thêm. Sau đó, lệnh gọi hàm tương tự đã cho chúng tôi kết quả mà chúng tôi mong đợi

Lưu ý rằng chúng tôi chưa bao giờ gọi bất kỳ thứ gì trong mô-đun method1()0 một cách rõ ràng và chúng tôi không sửa đổi bất kỳ thứ gì trong lệnh gọi tới DecoratorClass2. Chỉ cần nhập method1()0 đã khiến các chức năng mới đó được đăng ký và mở rộng chức năng của DecoratorClass2. Sử dụng kỹ thuật này cho phép chúng tôi phát triển một hệ thống rất lớn trong khi chỉ tập trung vào một phần nhỏ tại một thời điểm mà không phải lo lắng về khả năng tương tác của các phần khác. Nếu không có các bộ trang trí đăng ký và danh mục chức năng, việc thêm một chức năng kích hoạt mới sẽ cần sửa đổi mọi chức năng sử dụng kích hoạt

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êm

Phầ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

  • hoa văn trang trí
  • Tham khảo ngôn ngữ Python, Phần 8. 7,
  • PEP 318 – Trình trang trí cho các hàm và phương thức

Sách

  • Fluent Python, tái bản lần 2, bởi Luciano Ramalho

API

  • mô-đun funcools trong thư viện chuẩn Python

Tóm lược

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