簡體   English   中英

從另一個裝飾器創建裝飾器(python)

[英]creating decorator out of another decorator (python)

在python上的裝飾器主題上花了幾個小時之后,我仍然遇到兩個問題。

第一; 如果裝飾器沒有參數,則sytntax如下所示:

@decorator
def bye():
    return "bye"

這只是一種語法糖,與此相同

bye = decorator(bye)

但是如果我有一個帶參數的裝飾器:

@decorator(*args)
def bye():
    return "bye"

“無糖”版本看起來如何? 函數是否作為參數之一傳遞到內部?

bye = decorator("argument", bye)

第二個問題(與第一個更實際的示例有關);

def permission_required(permission):
    def wrap(function):
        @functools.wraps(function)
            def wrapped_func(*args, **kwargs):
                if not current_user.can(permission):
                    abort(403)
                return function(*args, **kwargs)
            return wrapped_function
    return wrap

def admin_required(f):
    return permission_required(Permission.ADMINISTER)(f)

在這里, Permission_required裝飾器傳遞給名為admin_required的新創建的裝飾器的返回語句。 我不知道這是如何工作的。 主要是我們返回原始裝飾器+函數的return語句(使用奇怪的語法)。 有人可以詳細說明嗎? -非常歡迎細節

帶有參數的裝飾器被簡單地調用(帶有該參數),以產生另一個裝飾器。 然后,像往常一樣,使用修飾的函數作為其參數來調用該修飾器。 因此翻譯為:

@decorator(*args)
def bye():
    return "bye"

將會:

bye = decorator(*args)(bye)

或者,也許您會發現更清晰的如下:

temp = decorator(*args)
bye = temp(bye)

(當然,實際上沒有創建temp變量。)

在第二個問題中,將@admin_required定義為@permission_required(Permission.ADMINISTER)的快捷方式。

當參數以修飾符形式給出時,

@decorator(a, b, c)
def function(): pass

它是寫作的語法糖

def function(): pass

function = decorator(a, b, c)(function)

也就是說, decorator使用參數a,b,c調用,然后它返回的對象使用唯一的參數function

當裝飾器是一個類時,最容易理解這是什么意思。 我將使用您的permission_required裝飾器作為運行示例。 可以這樣寫:

class permission_required:
    def __init__(self, permission):
        self.permission = permission

    def __call__(self, function):
        @functools.wraps(function)
        def wrapped_func(*args, **kwargs):
            if not current_user.can(permission):
                abort(403)
            return function(*args, **kwargs)
        return wrapped_func

admin_required = permission_required(Permission.ADMINISTER)

當您使用裝飾器時,例如

@permission_required(Permission.DESTRUCTIVE)
def erase_the_database():
    raise NotImplemented # TBD: should we even have this?

您首先實例化該類,將Permission.DESTRUCTIVE傳遞給__init__ ,然后將該實例作為一個函數調用,並以erase_the_database作為參數,該實例將調用__call__方法,該方法構造包裝的函數並返回它。

以這種方式思考, admin_required應該更容易理解:它是permission_required類的一個實例,尚未被調用。 基本上是速記:

@admin_required
def add_user(...): ...

而不是輸入

@permission_required(Permission.ADMINISTER)
def add_user(...): ...

現在,您擁有它的方式...

def permission_required(permission):
    def wrap(function):
        @functools.wraps(function)
            def wrapped_func(*args, **kwargs):
                if not current_user.can(permission):
                    abort(403)
                return function(*args, **kwargs)
            return wrapped_func
    return wrap

確實是寫同一件事的另一種方式。 permission_required返回wrap會隱式創建一個閉包對象 可以像函數一樣調用它,並且在執行時調用wrap 它記住傳遞給permission_requiredpermission值,以便wrap可以使用它。 這正是我上面顯示的課程所做的。 (實際上,像C ++和Rust這樣的編譯語言通常通過將閉包改為類定義來實現閉包,如我所展示的那樣。)

請注意, wrap本身具有相同功能! 我們可以進一步擴展它...

class permission_check_wrapper:
    def __init__(self, function, permission):
        self.function = function
        self.permission = permission
        functools.update_wrapper(self, function)

   def __call__(self, *args, **kwargs):
       if not current_user.can(permission):
           abort(403)
        return function(*args, **kwargs)

class permission_required:
    def __init__(self, permission):
        self.permission = permission

    def __call__(self, function):
        return permission_check_wrapper(self.permission, function)

或者我們可以使用functools.partial完成整個工作:

def permission_check_wrapper(*args, function, permission, **kwargs):
   if not current_user.can(permission):
       abort(403)
    return function(*args, **kwargs)

def wrap_fn_with_permission_check(function, *, permission):
    return functools.update_wrapper(
        functools.partial(permission_check_wrapper,
                          function=function,
                          permission=permission),
        wrapped=function)

def permission_required(permission):
    return functools.partial(wrap_fn_with_permission_check,
                             permission=permission)

定義@decorator(a,b,c) def foo以將@decorator(a,b,c) def foo foo = decorator(a,b,c)(foo) @decorator(a,b,c) def foo在於,該語言不在乎您選擇這幾種實現技術中的哪一種。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM