Hướng dẫn can we use decorator on class in python? - chúng ta có thể sử dụng decorator trên lớp trong python không?

Đây là một bản mở rộng về câu trả lời của Michael Speer để đưa nó thêm vài bước nữa:

Một trình trang trí phương thức thể hiện có các đối số và hành động trên một hàm với các đối số và giá trị trả về.

class Test(object):
    "Prints if x == y. Throws an error otherwise."
    def __init__(self, x):
        self.x = x

    def _outer_decorator(y):
        def _decorator(foo):
            def magic(self, *args, **kwargs) :
                print("start magic")
                if self.x == y:
                    return foo(self, *args, **kwargs)
                else:
                    raise ValueError("x ({}) != y ({})".format(self.x, y))
                print("end magic")
            return magic

        return _decorator

    @_outer_decorator(y=3)
    def bar(self, *args, **kwargs) :
        print("normal call")
        print("args: {}".format(args))
        print("kwargs: {}".format(kwargs))

        return 27

Và sau đó

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)

Hãy giả sử chúng ta cần một người trang trí mà chúng ta sẽ sử dụng trong một lớp cụ thể. Một cái gì đó như thế này:

Xác định trang trí này ở đâu?

Chức năng bên trong lớp

Sự thôi thúc ngay lập tức là đưa atomic_rating_change vào chính lớp:

Điều đó thực sự sẽ hoạt động, nhưng có một lỗ hổng lớn trong phương pháp này: atomic_rating_change trở thành một phương pháp thể hiện của lớp ____. Điều đó không có ý nghĩa gì. Hơn thế nữa, nó thậm chí không hoạt động như một phương pháp: nếu bạn gọi nó, tham số decorated sẽ được sử dụng là self.

Lưu ý rằng trong cơ thể lớp atomic_rating_change vẫn là một hàm đơn giản. Nó trở thành một phương pháp chỉ sau khi lớp học thực sự được tạo ra, và tại thời điểm này, người trang trí đã được áp dụng thành công.

Dưới đây là một ví dụ nhỏ có thể giúp bạn hiểu khái niệm này:

Bạn thực sự có thể thấy rằng func là một chức năng trong cơ thể lớp, nhưng nó là một phương pháp bên ngoài lớp. Hơn nữa, A.func and

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
0 Các giá trị cũng khác nhau, nhờ ma thuật mô tả Python (nếu bạn không quen thuộc với các mô tả trong Python, bạn nên kiểm tra liên kết). Trong Python 3
In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
1 chỉ là một chức năng đơn giản.

Chúng ta có thể làm gì để làm cho công việc này đúng? Đây là một số ý tưởng.

Làm cho nó tĩnh (don lồng)

Điều đơn giản bạn làm mỗi khi bạn muốn lưu trữ chỉ một hàm đơn giản trong một lớp chỉ là để biến nó thành một phương thức tĩnh.

Bạn có thể nghĩ rằng nó giải quyết hoàn toàn tất cả các vấn đề, nhưng thật đáng buồn là nó không có.

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
2 là một mô tả khác và các mô tả cư xử khác nhau trong và ngoài một lớp (yeah, một lần nữa).

Vì vậy, một khi bạn áp dụng bộ trang trí này vào một số phương pháp, bạn sẽ nhận được

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
3, bởi vì bạn thấy một mô tả thô bên trong lớp.

Tuyên bố bên ngoài

Giải pháp đơn giản nhất mà bạn có thể đã nghĩ đến là khai báo chức năng bên ngoài lớp.

Nhược điểm duy nhất là chức năng không bị ràng buộc với lớp nữa.

Tuyên bố trong một lớp khác

Để khắc phục điều này, chúng tôi có thể khai báo một lớp trợ giúp chứa tất cả các nhà trang trí cho User:

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
5 cũng không bị ràng buộc với lớp ban đầu (lưu tên), nhưng vì nó không còn là một hàm nữa, chúng ta có thể đặt nó vào User!

Tuyên bố trong lớp học bên trong

Bạn cũng có thể làm cho

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
7 riêng tư (bằng cách đổi tên nó thành
In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
8) nếu bạn không thích các lớp khác để sử dụng bộ trang trí của mình.

Giải pháp này khá vững chắc và được khuyến nghị, nhưng có một phương pháp hấp dẫn mà tôi nhận thức được.

Tạo lớp, không phải phương pháp

Nhờ Python

In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
9 Phương pháp ma thuật, bạn có thể khai báo bất kỳ người trang trí nào là một lớp, không phải là một chức năng. Nhớ lại, vấn đề ban đầu của chúng tôi là các chức năng hoạt động khác nhau trong và ngoài cơ thể lớp, nhưng điều đó không áp dụng cho các lớp bên trong cơ thể lớp (nhìn vào
In [2]:

    test = Test(3)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    normal call
    args: (13, 'Test')
    kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
Out[2]:
    27
In [3]:

    test = Test(4)
    test.bar(
        13,
        'Test',
        q=9,
        lollipop=[1,2,3]
    )
    ​
    start magic
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-3-576146b3d37e> in <module>()
          4     'Test',
          5     q=9,
    ----> 6     lollipop=[1,2,3]
          7 )

    <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
         11                     return foo(self, *args, **kwargs)
         12                 else:
    ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
         14                 print("end magic")
         15             return magic

    ValueError: x (4) != y (3)
7 trong các ví dụ trước: nó hoạt động tốt). Vì vậy, việc chuyển đổi người trang trí từ ví dụ đầu tiên sang một lớp học sẽ hoạt động hoàn hảo.

Sự thật thú vị: Cả hai giải pháp làm việc liên quan đến việc thay đổi hàm bên trong thành lớp bên trong (do hành vi cụ thể của các thuộc tính chức năng, DUH).

Lưu ý rằng vấn đề được mô tả không có tính chất khái niệm, nó chỉ là một cách để chống lại thực tế rằng một chức năng có thể là một thuộc tính lớp đơn giản trong Python.

Làm thế nào để bạn thực hiện một người trang trí trong một python lớp?

Để trang trí một hàm với một lớp, chúng ta phải sử dụng @syntax theo sau là tên lớp của chúng ta phía trên định nghĩa chức năng. Theo quy ước, chúng tôi sẽ sử dụng trường hợp lạc đà cho tên lớp của chúng tôi. Trong định nghĩa lớp, chúng tôi xác định hai phương thức: hàm tạo init và phương thức gọi ma thuật (hoặc dunder).use the @syntax followed by our class name above the function definition. Following convention, we will use camel-case for our class name. In the class definition, we define two methods: the init constructor and the magic (or dunder) call method.

Nơi nào chúng ta sử dụng các nhà trang trí trong Python?

Bạn sẽ sử dụng một bộ trang trí khi bạn cần thay đổi hành vi của một hàm mà không cần sửa đổi chức năng. Một vài ví dụ tốt là khi bạn muốn thêm ghi nhật ký, hiệu suất kiểm tra, thực hiện bộ nhớ đệm, xác minh quyền, v.v. Bạn cũng có thể sử dụng một khi bạn cần chạy cùng một mã trên nhiều chức năng.when you need to change the behavior of a function without modifying the function itself. A few good examples are when you want to add logging, test performance, perform caching, verify permissions, and so on. You can also use one when you need to run the same code on multiple functions.

Người trang trí được sử dụng để biểu thị một phương pháp lớp là gì?

Bộ trang trí @ClassMethod là một bộ trang trí chức năng tích hợp, là một biểu thức được đánh giá sau khi chức năng của bạn được xác định.Kết quả của việc đánh giá bóng tối định nghĩa chức năng của bạn.Một phương thức lớp nhận lớp là đối số đầu tiên ngầm, giống như một phương thức thể hiện nhận được thể hiện.@classmethod decorator is a built-in function decorator which is an expression that gets evaluated after your function is defined. The result of that evaluation shadows your function definition. A class method receives the class as the implicit first argument, just like an instance method receives the instance.