Python trang trí lồng nhau với các đối số

Khi tôi lần đầu tiên biết về các công cụ trang trí Python, việc sử dụng chúng giống như làm phép thuật voodoo. Trình trang trí có thể cung cấp cho bạn khả năng thêm các chức năng mới vào bất kỳ chức năng nào có thể gọi được mà không thực sự chạm vào hoặc thay đổi mã bên trong nó. Điều này thường có thể mang lại khả năng đóng gói tốt hơn và giúp bạn viết mã rõ ràng và dễ hiểu hơn. Tuy nhiên, decorator được coi là một chủ đề khá nâng cao trong Python vì để hiểu và viết nó yêu cầu bạn phải có lệnh đối với nhiều khái niệm bổ sung như đối tượng hạng nhất, hàm bậc cao hơn, bao đóng, v.v. Đầu tiên, tôi sẽ cố gắng giới thiệu những khái niệm này khi cần thiết và sau đó làm sáng tỏ khái niệm cốt lõi của lớp trang trí theo từng lớp. Vì vậy, hãy đi sâu vào

Đối tượng hạng nhất

Trong Python, về cơ bản mọi thứ đều là đối tượng và các hàm được coi là đối tượng hạng nhất. Điều đó có nghĩa là các hàm có thể được truyền xung quanh và được sử dụng làm đối số, giống như bất kỳ đối tượng nào khác (chuỗi, int, float, danh sách, v.v.). Bạn có thể gán các hàm cho các biến và xử lý chúng như bất kỳ đối tượng nào khác. Hãy xem xét ví dụ này

def func_a():
    return "I was angry with my friend."


def func_b():
    return "I told my wrath, my wrath did end"


def func_c(*funcs):
    for func in funcs:
        print(func())


main_func = func_c
main_func(func_a, func_b)

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end

Ví dụ trên minh họa cách Python coi các chức năng là công dân hạng nhất. Đầu tiên, tôi định nghĩa hai hàm,

>>> 'Hello from the inner func'
6 và
>>> 'Hello from the inner func'
7, sau đó
>>> 'Hello from the inner func'
8 lấy chúng làm tham số.
>>> 'Hello from the inner func'
8 chạy các hàm được lấy làm tham số và in kết quả. Sau đó, chúng tôi gán
>>> 'Hello from the inner func'
8 cho biến
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
1. Cuối cùng, chúng tôi chạy
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
1 và nó hoạt động giống như
>>> 'Hello from the inner func'
8

Hàm bậc cao hơn

Python cũng cho phép bạn sử dụng các hàm làm giá trị trả về. Bạn có thể nhận một hàm khác và trả về hàm đó hoặc bạn có thể định nghĩa một hàm bên trong một hàm khác và trả về hàm bên trong

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
0

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
1

Bây giờ bạn có thể gán kết quả của

def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
4 cho một biến khác và thực hiện chức năng đầu ra

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
3

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
4

Hãy xem xét một ví dụ khác nơi bạn có thể định nghĩa một hàm lồng trong một hàm và trả về hàm lồng thay vì kết quả của nó

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
5

>>> 'Hello from the inner func'

Lưu ý cách hàm lồng nhau

def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
5 được định nghĩa bên trong hàm
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
6 và sau đó câu lệnh return của hàm
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
6 trả về hàm lồng nhau. Sau khi định nghĩa, để đến hàm lồng nhau, đầu tiên chúng ta gọi hàm
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
6 và nhận kết quả là một hàm khác. Sau đó thực hiện kết quả của hàm
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
6 in ra thông báo từ hàm
def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients
5

đóng cửa

Bạn đã thấy các ví dụ về chức năng bên trong hoạt động trong phần trước. Các hàm lồng nhau có thể truy cập các biến của phạm vi kèm theo. Trong Python, các biến không cục bộ này chỉ được đọc theo mặc định và chúng ta phải khai báo chúng một cách rõ ràng là không cục bộ (sử dụng từ khóa

>>> 'Hello from the inner func'
31) để sửa đổi chúng. Sau đây là một ví dụ về hàm lồng nhau truy cập một biến không cục bộ

def burger(name):
    def ingredients():
        if name == "deli":
            return ("steak", "pastrami", "emmental")
        elif name == "smashed":
            return ("chicken", "nacho cheese", "jalapeno")
        else:
            return None

    return ingredients

Bây giờ chạy chức năng,

>>> 'Hello from the inner func'
3

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
0

Chà, thật bất thường

Hàm

>>> 'Hello from the inner func'
32 được gọi với chuỗi
>>> 'Hello from the inner func'
33 và hàm trả về được gắn với tên
>>> 'Hello from the inner func'
34. Khi gọi
>>> 'Hello from the inner func'
35, thông báo vẫn được ghi nhớ và được sử dụng để lấy kết quả mặc dù chức năng bên ngoài
>>> 'Hello from the inner func'
32 đã hoàn thành việc thực hiện

Kỹ thuật này mà một số dữ liệu (“deli”) được gắn vào mã được gọi là đóng trong Python. Giá trị trong phạm vi kèm theo được ghi nhớ ngay cả khi biến nằm ngoài phạm vi hoặc chính hàm đó bị xóa khỏi không gian tên hiện tại. Người trang trí sử dụng ý tưởng về các biến không cục bộ nhiều lần và bạn sẽ sớm thấy cách

Viết một Decorator cơ bản

Với những điều kiện tiên quyết này, hãy tiếp tục và tạo công cụ trang trí đơn giản đầu tiên của bạn

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
1

Trước khi sử dụng decorator, hãy xác định một hàm đơn giản không có bất kỳ tham số nào

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
2

Coi các chức năng là đối tượng hạng nhất, bạn có thể sử dụng trình trang trí của mình như thế này

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
3

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
4

Trong hai dòng trên, bạn có thể thấy một trình trang trí rất đơn giản đang hoạt động. Hàm

>>> 'Hello from the inner func'
37 của chúng tôi nhận một hàm mục tiêu, thao tác hàm mục tiêu bên trong hàm
>>> 'Hello from the inner func'
38 và sau đó trả về hàm
>>> 'Hello from the inner func'
38. Chạy hàm được trả về bởi trình trang trí, bạn sẽ nhận được kết quả đã sửa đổi của mình. Nói một cách đơn giản, các trình trang trí bao bọc một chức năng và sửa đổi hành vi của nó

Chức năng trang trí chạy tại thời điểm chức năng trang trí được nhập/xác định, không phải khi nó được gọi

Trước khi chuyển sang phần tiếp theo, hãy xem cách chúng ta có thể lấy giá trị trả về của hàm mục tiêu thay vì chỉ in nó

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
5

Trong ví dụ trên, hàm trình bao bọc trả về kết quả của hàm đích và chính trình bao bọc. Điều này giúp có thể nhận được kết quả của hàm đã sửa đổi

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
6

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
7

Bạn có đoán được tại sao giá trị trả về của hàm được trang trí lại xuất hiện ở dòng cuối cùng thay vì ở giữa như trước đây không?

Đường cú pháp @

Cách bạn đã sử dụng công cụ trang trí trong phần trước có thể cảm thấy hơi rắc rối. Trước tiên, bạn phải gõ tên

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
00 ba lần để gọi và sử dụng trình trang trí. Ngoài ra, việc phân biệt nơi trang trí đang thực sự hoạt động trở nên khó khăn hơn. Vì vậy, Python cho phép bạn sử dụng trình trang trí với cú pháp đặc biệt
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
01. Bạn có thể áp dụng các công cụ trang trí của mình trong khi xác định các chức năng của mình, như thế này

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
8

Đôi khi cú pháp trên được gọi là cú pháp chiếc bánh và nó chỉ là một cú pháp đường cho

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
02

Trang trí các chức năng với các đối số

Trình trang trí ngây thơ mà chúng tôi đã triển khai ở trên sẽ chỉ hoạt động đối với các hàm không có đối số. Nó sẽ thất bại và tăng

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
03 nếu bạn cố trang trí một hàm có đối số với
>>> 'Hello from the inner func'
37. Bây giờ, hãy tạo một trình trang trí khác có tên là
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
05 sẽ nhận một hàm trả về một giá trị chuỗi và chuyển đổi giá trị chuỗi đó thành chữ hoa

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
9

Tạo hàm mục tiêu trả về giá trị chuỗi

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
00

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
01

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
02

Hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06 lấy một tham số là
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
07 và trả về một thông báo dưới dạng chuỗi. Hãy xem cách trình trang trí
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
05 đang sửa đổi chuỗi trả về ban đầu, chuyển đổi chuỗi đó thành chữ hoa và thêm ký hiệu
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
09 bổ sung mà không thay đổi trực tiếp bất kỳ mã nào trong hàm
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06

Giải quyết khủng hoảng danh tính

Trong Python, bạn có thể xem xét bất kỳ đối tượng nào và các thuộc tính của nó thông qua trình bao tương tác. Một hàm biết danh tính của nó, chuỗi tài liệu, v.v. Chẳng hạn, bạn có thể kiểm tra hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
11 tích hợp theo các cách sau

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
03

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
04

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
05

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
07

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
08

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
09

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
10

Việc xem xét nội quan này hoạt động tương tự đối với các chức năng mà bạn đã tự xác định. Tôi sẽ sử dụng hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06 đã xác định trước đó

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
11

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
12

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
13

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
14

Bây giờ những gì đang xảy ra ở đó. Trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
05 đã làm cho chức năng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06 bối rối về danh tính của chính nó. Thay vì báo cáo tên của chính nó, nó lấy danh tính của hàm bên trong
>>> 'Hello from the inner func'
38. Điều này có thể gây nhầm lẫn trong khi gỡ lỗi. Bạn có thể sửa lỗi này bằng cách sử dụng trình trang trí
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
16 dựng sẵn từ mô-đun
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
17. Điều này sẽ đảm bảo rằng bản sắc ban đầu của chức năng được trang trí vẫn được bảo tồn

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
15

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
16

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
17

Xem xét kỹ hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06 được trang trí bằng trình trang trí đã sửa đổi sẽ cho bạn kết quả mong muốn

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
11

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
19

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
13

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
31

Trang trí trong tự nhiên

Trước khi chuyển sang phần tiếp theo, hãy xem một vài ví dụ thực tế về decorator. Để xác định tất cả các trình trang trí, chúng tôi sẽ sử dụng mẫu sau mà chúng tôi đã hoàn thiện cho đến nay

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
32

hẹn giờ

Trình trang trí hẹn giờ sẽ giúp bạn định thời gian cho các cuộc gọi của mình theo cách không xâm phạm. Nó có thể giúp bạn trong khi gỡ lỗi và định hình các chức năng của bạn

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
33

Theo cách trên, chúng ta có thể xem xét thời gian cần thiết để hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
19 hoàn thành quá trình thực thi của nó

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
34

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
35

Bộ ghi ngoại lệ

Cũng giống như trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
20, chúng ta có thể định nghĩa trình trang trí logger sẽ ghi lại trạng thái của một hàm có thể gọi được. Đối với phần trình diễn này, tôi sẽ xác định một trình ghi ngoại lệ sẽ hiển thị thông tin bổ sung như dấu thời gian, tên đối số khi một ngoại lệ xảy ra bên trong có thể gọi được trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
36

Hãy gọi ZeroDivisionError để xem hoạt động của bộ ghi nhật ký

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
37

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
38

Đầu tiên, trình trang trí in một vài thông tin liên quan đến chức năng và sau đó đưa ra lỗi ban đầu

Xác thực & Kiểm tra thời gian chạy

Hệ thống kiểu của Python được gõ mạnh nhưng rất năng động. Đối với tất cả các lợi ích của nó, điều này có nghĩa là một số lỗi có thể cố gắng len lỏi vào, mà các ngôn ngữ được nhập tĩnh hơn (như Java) sẽ bắt gặp tại thời điểm biên dịch. Nhìn xa hơn nữa, bạn có thể muốn thực thi các kiểm tra tùy chỉnh, phức tạp hơn đối với dữ liệu đi vào hoặc ra. Người trang trí có thể cho phép bạn dễ dàng xử lý tất cả những điều này và áp dụng nó cho nhiều chức năng cùng một lúc

Hãy tưởng tượng điều này. bạn có một tập hợp các hàm, mỗi hàm trả về một từ điển, từ điển này (trong số các trường khác) bao gồm một trường có tên là “tóm tắt. ” Giá trị của phần tóm tắt này không được dài quá 30 ký tự; . Đây là một công cụ trang trí làm tăng ValueError nếu điều đó xảy ra

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
39

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
40

Thử lại

Hãy tưởng tượng một tình huống trong đó khả năng gọi được xác định của bạn không thành công do một số vấn đề liên quan đến I/O và bạn muốn thử lại lần nữa. Decorator có thể giúp bạn đạt được điều đó theo cách có thể tái sử dụng. Hãy xác định một trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
21 sẽ chạy lại chức năng được trang trí nhiều lần nếu xảy ra lỗi http

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
41

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
42

Áp dụng nhiều trang trí

Bạn có thể áp dụng nhiều trình trang trí cho một chức năng bằng cách xếp chồng chúng lên nhau. Hãy xác định hai trình trang trí đơn giản và sử dụng cả hai trên một chức năng

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
43

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
44

Các decorator được gọi theo thứ tự từ dưới lên. Đầu tiên, trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
22 được áp dụng trên kết quả của hàm
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
23 và sau đó kết quả của
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
22 được chuyển đến trình trang trí
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
25. Ngăn xếp trang trí ở trên có thể được viết là
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
26. Thay đổi thứ tự của các trang trí và xem điều gì sẽ xảy ra

Trang trí với Arguments

Trong khi xác định trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
21 trong phần trước, bạn có thể nhận thấy rằng tôi đã mã hóa cứng số lần tôi muốn chức năng thử lại nếu xảy ra lỗi. Sẽ rất hữu ích nếu bạn có thể đưa số lần thử làm tham số vào trình trang trí và làm cho nó hoạt động tương ứng. Đây không phải là một nhiệm vụ tầm thường và bạn sẽ cần ba cấp hàm lồng nhau để đạt được điều đó

Trước khi làm điều đó, hãy tạo một ví dụ nhỏ về cách bạn có thể xác định các trình trang trí bằng các tham số

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
45

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
46

Trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
28 nhận một tham số duy nhất có tên là
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
29. Nó chia đầu ra chuỗi của hàm được trang trí bằng một khoảng trắng và sau đó nối chúng bằng dấu phân cách do người dùng xác định được chỉ định trong đối số
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
29. Định nghĩa lồng nhau ba lớp trông có vẻ đáng sợ nhưng chúng ta sẽ làm được điều đó ngay lập tức. Lưu ý cách bạn có thể sử dụng trình trang trí với các tham số khác nhau. Trong ví dụ trên, tôi đã định nghĩa ba hàm khác nhau để chứng minh việc sử dụng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
28. Điều quan trọng cần lưu ý là trong trường hợp trình trang trí nhận tham số, bạn sẽ luôn cần truyền một thứ gì đó cho nó và ngay cả khi bạn không muốn truyền bất kỳ tham số nào (chạy với mặc định), bạn vẫn cần phải trang trí . Hãy thử thay đổi decorator trên hàm
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
34 từ
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
35 thành
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
28 và xem điều gì sẽ xảy ra

Thông thường, một trình trang trí tạo và trả về một hàm bao bọc bên trong nhưng ở đây trong trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
37, có một hàm bên trong bên trong một hàm bên trong khác. Điều này gần giống như một giấc mơ trong một giấc mơ từ bộ phim Inception

Có một vài điều tế nhị xảy ra trong hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
35

  • Xác định

    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    39 là một hàm bên trong có nghĩa là
    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    40 sẽ tham chiếu đến một đối tượng hàm
    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    41

  • Đối số

    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    29 dường như không được sử dụng trong chính
    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    35. Nhưng bằng cách chuyển
    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    29, một bao đóng được tạo trong đó giá trị của
    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    29 được lưu trữ cho đến khi nó sẽ được sử dụng sau bởi
    >>> I was angry with my friend.
    >>> I told my wrath, my wrath did end
    
    46

Trang trí có và không có đối số

Bạn đã thấy trước đó rằng một trình trang trí được thiết kế đặc biệt để lấy tham số không thể được sử dụng nếu không có tham số; . Nhưng nếu bạn muốn thiết kế một cái có thể sử dụng cả có và không có đối số thì sao?. Hãy xác định lại trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
28 để bạn có thể sử dụng nó với các tham số hoặc giống như một trình trang trí ít tham số thông thường mà chúng ta đã thấy trước đây

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
47

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
48

Ở đây, đối số

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
49 đóng vai trò là điểm đánh dấu, cho biết liệu trình trang trí có được gọi với đối số hay không

Nếu

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
28 đã được gọi mà không có đối số, hàm được trang trí sẽ được chuyển vào dưới dạng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
49. Nếu nó đã được gọi với các đối số, thì
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
49 sẽ là Không có. Dấu * trong danh sách đối số có nghĩa là các đối số còn lại không thể được gọi là đối số vị trí. Lần này bạn có thể sử dụng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
28 có hoặc không có đối số và chức năng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
06 và
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
22 ở trên chứng minh rằng

Một mẫu chung

Cá nhân tôi thấy nó cồng kềnh khi bạn cần ba lớp hàm lồng nhau để xác định một trình trang trí tổng quát có thể được sử dụng có hoặc không có đối số. David Beazly trong cuốn sách Python Cookbook của anh ấy cho thấy một cách tuyệt vời để xác định các trình trang trí tổng quát mà không cần viết ba cấp hàm lồng nhau. Nó sử dụng hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
56 tích hợp để đạt được điều đó. Sau đây là mẫu bạn có thể sử dụng để xác định các trình trang trí tổng quát theo cách thanh lịch hơn

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
49

Hãy xác định lại trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
21 của chúng tôi bằng cách sử dụng mẫu này

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
50

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
51

Trong trường hợp này, bạn không cần phải viết các hàm lồng ba cấp độ và

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
56 sẽ lo việc đó. Các phần có thể được sử dụng để tạo các hàm dẫn xuất mới có một số tham số đầu vào được gán trước. Khoảng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
59 làm như sau

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
52

Điều này giúp loại bỏ sự cần thiết phải viết nhiều lớp chức năng xuất xưởng lồng nhau, có được một trình trang trí tổng quát

Xác định Decorators với các lớp

Lần này, tôi sẽ sử dụng một class để soạn một decorator. Các lớp có thể hữu ích để tránh kiến ​​trúc lồng nhau trong khi viết các bộ trang trí. Ngoài ra, có thể hữu ích khi sử dụng một lớp trong khi viết các trình trang trí trạng thái. Bạn có thể làm theo mẫu bên dưới để soạn các trang trí với các lớp

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
53

Hãy sử dụng mẫu trên để viết một trình trang trí có tên là

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
60 sẽ thêm các thẻ in đậm
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
61 vào đầu ra chuỗi của một hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
54

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
55

Phương thức init() lưu trữ một tham chiếu đến hàm num_calls và có thể thực hiện các khởi tạo cần thiết khác. Phương thức call() sẽ được gọi thay vì hàm được trang trí. Về cơ bản, nó thực hiện giống như hàm

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
62 trong các ví dụ trước đây của chúng tôi. Lưu ý rằng bạn cần sử dụng hàm
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
63 thay vì
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
64

Trước khi tiếp tục, hãy viết một trình trang trí trạng thái bằng cách sử dụng các lớp. Các nhà trang trí có trạng thái có thể nhớ trạng thái của lần chạy trước đó của họ. Đây là một công cụ trang trí có trạng thái có tên là

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
65 sẽ theo dõi số lần các chức năng được trang trí được gọi trong từ điển. Các khóa của từ điển sẽ chứa tên của các chức năng và các giá trị tương ứng sẽ chứa số lượng cuộc gọi

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
56

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
57

Một vài ví dụ khác

Giá trị trả về bộ đệm

Trình trang trí có thể cung cấp một cách thanh lịch để ghi nhớ các giá trị trả về của hàm. Hãy tưởng tượng bạn có một API đắt tiền và bạn muốn gọi nó càng ít lần càng tốt. Ý tưởng là lưu và lưu vào bộ đệm các giá trị do API trả về cho các đối số cụ thể, để nếu các đối số đó xuất hiện lại, bạn có thể cung cấp kết quả từ bộ đệm thay vì gọi lại API. Điều này có thể cải thiện đáng kể hiệu suất của ứng dụng của bạn. Ở đây tôi đã mô phỏng một lệnh gọi API đắt tiền và cung cấp bộ nhớ đệm với một trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
58

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
59

Bạn sẽ thấy rằng việc chạy chức năng này mất khoảng 3 giây. Để cache kết quả, chúng ta có thể sử dụng funcools tích hợp sẵn của Python. lru_cache để lưu kết quả đối với một đối số trong từ điển và phục vụ kết quả đó khi nó gặp lại đối số tương tự. Hạn chế duy nhất ở đây là, tất cả các đối số cần phải được băm

>>> 'Hello from the inner func'
0

>>> 'Hello from the inner func'
1

Bộ nhớ đệm ít sử dụng gần đây nhất (LRU) sắp xếp các mục theo thứ tự sử dụng, cho phép bạn nhanh chóng xác định mục nào không được sử dụng trong khoảng thời gian dài nhất. Trong trường hợp trên, tham số

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
66 đề cập đến số lượng phản hồi tối đa được lưu trước khi bắt đầu xóa những phản hồi sớm nhất. Trong khi bạn chạy chức năng trang trí, bạn sẽ thấy lần đầu tiên sẽ mất khoảng 3 giây để trả về kết quả. Nhưng nếu bạn chạy lại chức năng với cùng một tham số, nó sẽ lấy kết quả từ bộ đệm gần như ngay lập tức

chuyển đổi đơn vị

Trình trang trí sau chuyển đổi độ dài từ đơn vị SI sang nhiều đơn vị khác mà không làm ô nhiễm chức năng mục tiêu của bạn với logic chuyển đổi

>>> 'Hello from the inner func'
2

Hãy sử dụng nó trên một hàm trả về diện tích hình chữ nhật

>>> 'Hello from the inner func'
3

>>> 'Hello from the inner func'
4

Sử dụng công cụ trang trí chuyển đổi trên hàm vùng cho biết cách nó in ra đơn vị chuyển đổi trước khi trả về kết quả mong muốn. Thử nghiệm với các đơn vị chuyển đổi khác và xem điều gì sẽ xảy ra

Đăng ký chức năng

Sau đây là một ví dụ về đăng ký chức năng logger trong khung Flask. Trình trang trí

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
67 không thực hiện bất kỳ thay đổi nào đối với chức năng
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
68 được trang trí. Thay vào đó, nó nhận chức năng và đăng ký nó trong một danh sách có tên là
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
69 mỗi khi nó được gọi

>>> 'Hello from the inner func'
5

Nếu bạn chạy máy chủ và nhấn vào url

>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
70, nó sẽ chào đón bạn bằng một thông báo
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
71. Ngoài ra, bạn sẽ có thể thấy bản in
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
72 và
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
73 của yêu cầu http của mình trên thiết bị đầu cuối. Hơn nữa, nếu bạn kiểm tra
>>> I was angry with my friend.
>>> I told my wrath, my wrath did end
69, bạn sẽ tìm thấy thiết bị ghi nhật ký đã đăng ký ở đó. Bạn sẽ tìm thấy nhiều cách sử dụng decorator thực tế hơn trong Flask framework

Nhận xét

Tất cả các đoạn mã trong blog đã được viết và thử nghiệm với python 3. 8 trên máy chạy Ubuntu 20. 04

Trình trang trí trong Python có thể chấp nhận đối số không?

Các đối số của trình trang trí có thể truy cập được đối với trình trang trí bên trong thông qua một bao đóng , chính xác như cách hàm bên trong wrap() có thể truy cập f. Và vì các bao đóng mở rộng đến tất cả các cấp độ của các hàm bên trong, nên arg cũng có thể truy cập được từ bên trong wrap() nếu cần.

Một chức năng có thể có 2 trang trí?

Vì vậy, ở đây trong bài đăng này, chúng ta sẽ tìm hiểu về Decorator Chaining. Chuỗi trang trí có nghĩa là áp dụng nhiều hơn một trang trí bên trong một chức năng. Python cho phép chúng ta triển khai nhiều trình trang trí cho một hàm .

Có bao nhiêu trình trang trí có thể được áp dụng cho một hàm trong Python?

Chà, có 1000 khả năng . Việc sử dụng cổ điển đang mở rộng một hành vi chức năng từ một lib bên ngoài (bạn không thể sửa đổi nó) hoặc để gỡ lỗi (bạn không muốn sửa đổi nó vì nó là tạm thời). Tất nhiên, điểm hay của decorator là bạn có thể sử dụng chúng ngay lập tức trên hầu hết mọi thứ mà không cần viết lại.

Câu nào sau đây là đúng Bạn không thể xâu chuỗi nhiều bộ trang trí trong Python?

Giải thích. Mọi chức năng, bất kể có tham số hay không, đều có thể được trang trí . Do đó tuyên bố là sai.