Hướng dẫn python decorator log function arguments - đối số chức năng nhật ký trang trí python

Đây là phiên bản cập nhật cho Python 3.6+

import inspect


def dump_args[func]:
    """
    Decorator to print function call details.

    This includes parameters names and effective values.
    """

    def wrapper[*args, **kwargs]:
        func_args = inspect.signature[func].bind[*args, **kwargs].arguments
        func_args_str = ", ".join[map["{0[0]} = {0[1]!r}".format, func_args.items[]]]
        print[f"{func.__module__}.{func.__qualname__} [ {func_args_str} ]"]
        return func[*args, **kwargs]

    return wrapper


@dump_args
def test[a, b=4, c="blah-blah", *args, **kwargs]:
    pass


test[1]
test[1, 3]
test[1, d=5]
test[1, 2, 3, 4, 5, d=6, g=12.9]

Phiên bản cũ

Phiên bản làm việc với các giá trị mặc định:

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]

Result:

>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]

Bất kỳ ứng dụng sản xuất nào có thể sẽ có một số hướng dẫn về cách thức và những gì cần phải đăng nhập vào ứng dụng của bạn. Thông thường, các hướng dẫn này xuất phát từ các mô hình công nghiệp chung như nhật ký của tất cả các ngoại lệ. Tuy nhiên, việc thực hiện các hướng dẫn này được để lại cho các nhà phát triển riêng lẻ và dẫn đến cùng một bộ báo cáo ghi nhật ký được lặp lại trong suốt cơ sở mã. Ví dụ, để ghi nhật ký tất cả các trường hợp ngoại lệ, bạn sẽ có một câu lệnh ghi nhật ký trong mỗi khối

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
9 nắm bắt ngoại lệ và ghi nhật ký theo cấp độ
>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
0. Nhưng tuyên bố ghi nhật ký cho cùng một kịch bản có thể khác nhau giữa các nhà phát triển do phong cách phát triển cá nhân của họ. Làm thêm giờ Điều này dẫn đến việc đăng nhập phân mảnh và không nhất quán trong ứng dụng. Hơn nữa, các nhà phát triển có thể phạm sai lầm và bỏ lỡ tuyên bố đăng nhập tại những nơi cần thiết.

Một cách tiếp cận để giảm bớt vấn đề này là bằng cách sử dụng tính năng trang trí Python. Bài viết này sẽ cung cấp một cái nhìn tổng quan ngắn gọn về các nhà trang trí và trình diễn cách tạo ra một nhà trang trí để trừu tượng các tuyên bố khai thác gỗ phổ biến này. Bạn có thể đọc thêm về trang trí và nhiều cách mà chúng có thể được sử dụng trong mồi tuyệt vời này trên trang trí Python.

Người trang trí là gì

Một người trang trí là một chức năng có một chức năng khác và mở rộng hành vi của nó mà không cần sửa đổi rõ ràng nó. Chúng còn được gọi là chức năng bậc cao.

Các chức năng của Python là những công dân hạng nhất. Điều này có nghĩa là các chức năng có thể được thông qua như đối số hoặc có thể là chủ đề của nhiệm vụ. Vì vậy, nếu bạn có chức năng

>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
1, bạn có thể sử dụng nó như bất kỳ đối tượng nào khác và đi sâu vào các thuộc tính của nó.

def sum[a, b=10]:
    return a+b

>>> sum

>>> sum.__code__.co_varnames  # Names of local variables
['a', 'b']

Vì các chức năng hoạt động như một đối tượng, bạn có thể gán

>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
2 cho một hàm khác. Sau đó, gọi
>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
2 sẽ gọi chức năng này thay vì cái mà chúng tôi đã xác định trước đó. Các trình trang trí sử dụng hành vi này bằng cách gán
>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
2 một hàm mới lấy
>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
2 làm tham số và kết thúc một số logic bổ sung xung quanh nó do đó mở rộng nó mà không cần sửa đổi chức năng.

def my_decorator[func]:
    def wrapper[*args, **kwargs]:
        # do something before `sum`
        result = func[*args, **kwargs]
        # do something after `sum`
        return result
    return wrapper

sum = my_decorator[sum]

>>> sum

Mô hình này phổ biến đến mức Python có đường cú pháp để trang trí một chức năng. Vì vậy, thay vì

>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
6, chúng ta có thể sử dụng ký hiệu
>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
7 trên đầu phương thức
>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
2 như thế này -

@my_decorator # Equivalent to `sum = my_decorator[sum]` after the method
def sum[a, b=10]:
    return a+b

Ghi nhật ký trang trí

Chúng tôi sẽ tạo ra một trình trang trí xử lý hai kịch bản ghi nhật ký phổ biến - các ngoại lệ ghi nhật ký là đối số phương thức lỗi và ghi nhật ký dưới dạng nhật ký gỡ lỗi.

Hãy bắt đầu bằng cách nắm bắt các ngoại lệ và đăng nhập nó bằng thư viện Python

>>> test  [  a = 1, b = 4, c = 'blah-blah' ]
test  [  a = 1, b = 3, c = 'blah-blah' ]
test  [  a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} ]
test  [  a = 1, b = 2, c = 3, args = [4, 5], kwargs = {'d': 6, 'g': 12.9} ]
9.

import functools
import logging

logging.basicConfig[level = logging.DEBUG]
logger = logging.getLogger[]

def log[func]:
    @functools.wraps[func]
    def wrapper[*args, **kwargs]:
        try:
            result = func[*args, **kwargs]
            return result
        except Exception as e:
            logger.exception[f"Exception raised in {func.__name__}. exception: {str[e]}"]
            raise e
    return wrapper

@log
def foo[]:
    raise Exception["Something went wrong"]

ERROR:root:Exception raised in foo. exception: Something went wrong
Traceback [most recent call last]:
  File "/foo.py", line 15, in wrapper
    result = func[*args, **kwargs]
  File "/foo.py", line 28, in foo
    raise Exception["Something went wrong"]
Exception: Something went wrong

Ngoài việc thiết lập

def sum[a, b=10]:
    return a+b
0, chúng tôi cũng đã sử dụng @functools.wraps. Bộ trang trí
def sum[a, b=10]:
    return a+b
1 cập nhật chức năng
def sum[a, b=10]:
    return a+b
2 trông giống như
def sum[a, b=10]:
    return a+b
3. Công cụ trang trí
def sum[a, b=10]:
    return a+b
4 của chúng tôi hiện có thể được sử dụng trên bất kỳ chức năng nào để bắt mọi ngoại lệ từ chức năng được bọc và đăng nhập nó một cách nhất quán.

Vì hàm

def sum[a, b=10]:
    return a+b
2 chấp nhận tất cả các đối số [
def sum[a, b=10]:
    return a+b
6], bộ trang trí
def sum[a, b=10]:
    return a+b
4 có thể được mở rộng để nắm bắt tất cả các tham số được truyền cho hàm được trang trí. Chúng ta có thể làm điều này chỉ bằng cách lặp lại trên các arg và kwarg và tham gia chúng để tạo thông báo chuỗi vào nhật ký.

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
0

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
1

Chúng tôi đăng nhập các tham số ở cấp độ gỡ lỗi vì chúng tôi không muốn nhật ký của chúng tôi lộn xộn với tất cả các đối số chức năng. Ghi nhật ký có thể được chuyển đổi trên các hệ thống của chúng tôi khi cần thiết. Hãy nhớ rằng điều này sẽ viết tất cả các giá trị đối số vào nhật ký bao gồm mọi dữ liệu hoặc bí mật PII.Keep in mind that this will write all argument values into log including any PII data or secrets.

Bộ trang trí khai thác cơ bản này có vẻ tốt và đã làm những gì chúng tôi đặt ra ban đầu để đạt được. Miễn là một phương pháp được trang trí với

def sum[a, b=10]:
    return a+b
4, chúng tôi sẽ ghi lại bất kỳ ngoại lệ nào được nêu trong đó và tất cả các đối số được truyền cho nó.

Tuy nhiên, trong một dự án thực sự,

def sum[a, b=10]:
    return a+b
0 có thể được trừu tượng hóa vào lớp của chính nó để khởi tạo một logger dựa trên cấu hình nhất định [chẳng hạn như đăng nhập vào một đám mây]. Trong trường hợp này, vô dụng để đăng nhập vào bảng điều khiển bằng cách tạo bộ ghi nhật ký của chúng tôi trong bộ trang trí
def sum[a, b=10]:
    return a+b
4. Chúng tôi cần một cách để chuyển
def sum[a, b=10]:
    return a+b
0 hiện có vào người trang trí của chúng tôi trong thời gian chạy. Để làm điều này, chúng tôi có thể mở rộng người trang trí
def sum[a, b=10]:
    return a+b
4 để chấp nhận
def sum[a, b=10]:
    return a+b
0 như một cuộc tranh luận.

Để bắt chước kịch bản này, chúng ta sẽ bắt đầu với một lớp tạo ra một bộ ghi âm cho chúng ta. Bây giờ chúng tôi sẽ tạo bộ ghi âm cơ bản nhưng bạn có thể tưởng tượng lớp cấu hình hành vi của logger theo yêu cầu.

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
2

Vì tại thời điểm viết người trang trí, chúng tôi không biết liệu chức năng cơ bản sẽ vượt qua chúng tôi

>>> sum

>>> sum.__code__.co_varnames  # Names of local variables
['a', 'b']
4 hoặc
>>> sum

>>> sum.__code__.co_varnames  # Names of local variables
['a', 'b']
5 hay không có logger nào, người trang trí chung của chúng tôi sẽ có thể xử lý tất cả chúng.

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
3

Mã trên trông khá đáng sợ nhưng hãy để tôi tóm tắt nó. Nhà trang trí

def sum[a, b=10]:
    return a+b
4 hiện xử lý ba kịch bản khác nhau -

  • Không có logger nào được thông qua: Đây là kịch bản tương tự những gì chúng tôi đã làm cho đến trước đó. Bộ trang trí chỉ đơn giản được sử dụng làm tuyên bố

    def sum[a, b=10]:
        return a+b
    
    4 trên đầu hàm. Trong trường hợp này, người trang trí nhận được một logger bằng cách gọi phương thức
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    8 và sử dụng nó cho phần còn lại của phương thức.
    : This is same scenario what we have been doing up until before this. The decorator is simply used as
    def sum[a, b=10]:
        return a+b
    
    4 statement on top of the function. In this case the decorator gets a logger by calling
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    8 method and uses it for rest of the method.

  • >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4 được thông qua: Công cụ trang trí
    def sum[a, b=10]:
        return a+b
    
    4 của chúng tôi hiện có thể chấp nhận ví dụ
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4 làm đối số. Sau đó, nó có thể gọi phương thức
    def my_decorator[func]:
        def wrapper[*args, **kwargs]:
            # do something before `sum`
            result = func[*args, **kwargs]
            # do something after `sum`
            return result
        return wrapper
    
    sum = my_decorator[sum]
    
    2 để tạo một bộ ghi chép lồng nhau và sử dụng phần còn lại của nó.
    : Our
    def sum[a, b=10]:
        return a+b
    
    4 decorator can now accept instance of
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4 as an argument. It can then call
    def my_decorator[func]:
        def wrapper[*args, **kwargs]:
            # do something before `sum`
            result = func[*args, **kwargs]
            # do something after `sum`
            return result
        return wrapper
    
    sum = my_decorator[sum]
    
    2 method to create a nested logger and use it rest of the way.

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
4

  • >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    5 được thông qua: Trong kịch bản thứ ba này, chúng ta có thể vượt qua bản ghi chính thay vì vượt qua lớp
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4.
    : In this third scenario we can pass the logger itself instead of passing
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4 class.

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
5

Chúng tôi vẫn chưa hoàn thành. Ngay cả trong hình thức hiện tại của chúng tôi trang trí log của chúng tôi bị hạn chế. Một hạn chế là chúng ta phải có sẵn

def sum[a, b=10]:
    return a+b
0 hoặc
>>> sum

>>> sum.__code__.co_varnames  # Names of local variables
['a', 'b']
4 trước phương pháp chúng ta muốn trang trí. Nói cách khác, tham chiếu đến logger phải tồn tại trước khi phương thức tồn tại. Điều này có thể hoạt động trong trường hợp hàm đích là một phần của lớp và phương pháp lớp
def my_decorator[func]:
    def wrapper[*args, **kwargs]:
        # do something before `sum`
        result = func[*args, **kwargs]
        # do something after `sum`
        return result
    return wrapper

sum = my_decorator[sum]
7 có thể khởi tạo logger, nhưng nó đã giành được công việc với các chức năng bên ngoài bối cảnh của lớp. Trong nhiều ứng dụng trong thế giới thực, chúng tôi sẽ không có từng mô -đun hoặc chức năng tạo ra logger của riêng họ. Thay vào đó, chúng tôi có thể muốn chuyển bộ ghi vào chức năng.
def sum[a, b=10]:
    return a+b
0 hoặc
>>> sum

>>> sum.__code__.co_varnames  # Names of local variables
['a', 'b']
4 sẽ được đưa vào các phương pháp hạ nguồn. Nói cách khác, một hàm có thể có logger được truyền cho nó trong tham số của nó.

Nhưng nếu hàm là một phần của lớp thì

def sum[a, b=10]:
    return a+b
0 sẽ được đưa vào lớp và không vào mọi phương pháp của lớp. Trong trường hợp này, chúng tôi sẽ muốn sử dụng logger có sẵn cho lớp của chúng tôi thay thế.

Vì vậy, mục tiêu của chúng tôi là nắm bắt

def sum[a, b=10]:
    return a+b
0 được thông qua như là lập luận cho chức năng được trang trí hoặc được chuyển cho hàm tạo lớp của chức năng được trang trí của chúng tôi và sử dụng nó để đăng nhập từ chính bộ trang trí. Bằng cách thực hiện điều này, người trang trí của chúng tôi có thể được tách rời hoàn toàn từ chính bộ ghi và sẽ sử dụng bất kỳ logger nào có sẵn cho phương pháp cơ bản trong thời gian chạy.or passed to the class constructor of our decorated function, and use it to log from the decorator itself. By doing this our decorator can be completely decoupled from the logger itself and will utilize whatever logger is available to the underlying method at runtime.

Để làm điều này, chúng tôi sẽ lặp lại đối số

>>> sum

2 và
>>> sum

3 và kiểm tra xem chúng tôi có nhận được
def sum[a, b=10]:
    return a+b
0 trong bất kỳ ai trong số họ không. Để kiểm tra xem hàm này có phải là một phần của lớp không, chúng ta có thể kiểm tra xem đối số đầu tiên của
>>> sum

2 có thuộc tính
>>> sum

6 không. Nếu đối số đầu tiên có thuộc tính
>>> sum

6, chúng tôi sẽ lặp lại trên
>>> sum

8 và kiểm tra xem một trong những giá trị này có phải là logger của chúng tôi không. Cuối cùng, nếu không có gì hoạt động, chúng tôi sẽ mặc định theo phương thức
>>> sum

>>> sum.__code__.co_varnames  # Names of local variables
['a', 'b']
8.

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
6

Người trang trí trên đủ chung chung để làm việc cho 2 kịch bản nữa ngoài 3 kịch bản chúng ta đã thảo luận trước đây -

  • def sum[a, b=10]:
        return a+b
    
    0 hoặc
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4 được chuyển sang phương pháp trang trí

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
7

  • Hoặc
    def sum[a, b=10]:
        return a+b
    
    0 hoặc
    >>> sum
    
    >>> sum.__code__.co_varnames  # Names of local variables
    ['a', 'b']
    
    4 được chuyển đến phương pháp
    def my_decorator[func]:
        def wrapper[*args, **kwargs]:
            # do something before `sum`
            result = func[*args, **kwargs]
            # do something after `sum`
            return result
        return wrapper
    
    sum = my_decorator[sum]
    
    7 lưu trữ chức năng được trang trí

def dumpArgs[func]:
    '''Decorator to print function call details - parameters names and effective values'''
    def wrapper[*func_args, **func_kwargs]:
        arg_names = func.func_code.co_varnames[:func.func_code.co_argcount]
        args = func_args[:len[arg_names]]
        defaults = func.func_defaults or []
        args = args + defaults[len[defaults] - [func.func_code.co_argcount - len[args]]:]
        params = zip[arg_names, args]
        args = func_args[len[arg_names]:]
        if args: params.append[['args', args]]
        if func_kwargs: params.append[['kwargs', func_kwargs]]
        print func.func_name + ' [' + ', '.join['%s = %r' % p for p in params] + ' ]'
        return func[*func_args, **func_kwargs]
    return wrapper  

@dumpArgs
def test[a, b = 4, c = 'blah-blah', *args, **kwargs]:
    pass

test[1]
test[1, 3]
test[1, d = 5]
test[1, 2, 3, 4, 5, d = 6, g = 12.9]
8

Một điều bổ sung chúng tôi đã làm là bọc tất cả các mã trước khi gọi hàm được trang trí

def sum[a, b=10]:
    return a+b
3 trong khối
@my_decorator # Equivalent to `sum = my_decorator[sum]` after the method
def sum[a, b=10]:
    return a+b
6. Chúng tôi không muốn thực hiện thất bại do các vấn đề trong việc đăng nhập ngay cả trước khi chức năng đích được gọi. Trong mọi trường hợp logic ghi nhật ký của chúng tôi sẽ gây ra lỗi trong hệ thống.

Sự kết luận

Bộ trang trí trên là một điểm khởi đầu tốt và có thể được mở rộng hoặc đơn giản hóa theo các yêu cầu. Nó làm giảm khả năng bỏ lỡ việc ghi nhật ký ngoại lệ và chuẩn hóa các thông báo lỗi trên khắp ứng dụng.

Tiếp cận với tôi trong các ý kiến ​​dưới đây cho bất kỳ câu hỏi mà bạn có thể có.

Người trang trí Python có thể lấy tranh luận không?

Các đối số trang trí có thể truy cập được cho bộ trang trí bên trong thông qua việc đóng, giống hệt như cách chức năng bên trong được gói [] có thể truy cập f. Và vì việc đóng cửa mở rộng đến tất cả các cấp của các hàm bên trong, ARG cũng có thể truy cập được từ bên trong Wrapping [] nếu cần thiết., exactly like how the wrapped[] inner function can access f . And since closures extend to all the levels of inner functions, arg is also accessible from within wrapped[] if necessary.

Ưu điểm lớn nhất của người trang trí trong Python là gì?

Một người trang trí trong Python là một chức năng lấy một chức năng khác làm đối số của nó và trả về một chức năng khác. Các nhà trang trí có thể cực kỳ hữu ích vì chúng cho phép mở rộng chức năng hiện có, mà không có bất kỳ sửa đổi nào đối với mã nguồn chức năng ban đầu.allow the extension of an existing function, without any modification to the original function source code.

Chức năng trang trí trong Python là gì?

Trình trang trí là một mẫu thiết kế trong Python cho phép người dùng thêm chức năng mới vào một đối tượng hiện có mà không cần sửa đổi cấu trúc của nó.Người trang trí thường được gọi trước khi định nghĩa về một chức năng bạn muốn trang trí.a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.

Loại trả lại của người trang trí là gì?

Người trang trí chấp nhận một hàm và trả về hàm hàm này chấp nhận một số và trả về đúng hay sai tùy thuộc vào việc số đó có phải là số nguyên tố hay không.either True or False depending on whether that number is prime or not.

Bài Viết Liên Quan

Chủ Đề