簡體   English   中英

Python裝飾器參數

[英]Python decorator parameters

我熟悉python裝飾器,並想知道一般情況。

假設我有一個生成人的函數:

def GeneratePerson(name, surname):
    return {"name": name, "surname": surname}

現在,假設我希望裝飾者添加年齡:

def WithAge(fn):
    def AddAge(*args):
        person = fn(args[0], args[1])
        person[age] = args[2]
        return person
    return AddAge

然后我會打電話

@AddAge
GeneratePerson("John", "Smith", 42)

但是,我發現這有點違反直覺。 如果查看“ GeneratePerson”的簽名,我會看到它僅帶有一個名稱和一個姓參數。 年齡(42)是裝飾者要求的屬性。 我覺得語法(Java風格)為:

@AddAge(42)
GeneratePerson("John", "Smith")

可能更可取。

我很欣賞我可以使用kwargs解決這個問題,將堆棧稱為:

@WithAge
@AddGender
GeneratePerson(name="John", surname="Smith", age="42", gender="M")

但是,有沒有一種方法可以直接將參數傳遞給裝飾器函數(例如: @Age(42)而不是必須通過裝飾函數間接傳遞參數)?

當心:裝飾器在定義時而不是在運行時應用。 這意味着您可以寫的是:

@AddAge
def GeneratePerson(name, lastname):
    ...

這足以回答您的最后一個問題:不能將參數傳遞給裝飾器而不是裝飾函數,除非您希望所有裝飾函數調用的參數都相同。

但是在Python 3中,可以向修飾后的函數添加參數:

def AddAge(param_name='age'):
    # define the new parameter
    param = inspect.Parameter(param_name,
                  inspect.Parameter.POSITIONAL_OR_KEYWORD)
    def wrapper(f):
        sig = inspect.signature(f)
        params = list(sig.parameters.values())
        params.append(param)                     # add the new parameter
        sig2 = sig.replace(parameters = params)  # to a new signature
        def wrapped(*args, **kwargs):
            bound = sig2.bind(*args, **kwargs)   # process the parameters
            args = bound.args                    #  both positional
            kwargs = bound.kwargs                #  and keywords
            if param_name in kwargs:             # extract the age
                age = kwargs[param_name]         #  either keyword
                del kwargs[param_name]
            else:
                args = list(args)                #  or positional
                age = args.pop()
            obj = f(*args, **kwargs)             # call the original function
            obj.age = age                        # add the attribute
            return obj
        wrapped.__signature__ = sig2             # attach the new signature
        return wrapped
    return wrapper

然后可以用這種方式

>>> f = AddAge()(GeneratePerson)
>>> p = f('John', 'Smith', 42)
>>> p.name
'John'
>>> p.lastname
'Smith'
>>> p.age
42

它甚至可以在函數調用中支持關鍵字:

>>> p = f('John', age=42, lastname="Smith")

將給出預期的結果。 唯一的限制是原始函數不得具有名為age的參數。 如果有,則必須將另一個名稱傳遞給裝飾器。

暫無
暫無

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

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