Как я могу рассматривать позиционные аргументы как ключевые аргументы в Python 2 - PullRequest
5 голосов
/ 05 июля 2011

Для декоратора, которого я пишу, я хотел бы манипулировать определенным именованным параметром функции.Рассмотрим следующий декоратор:

def square_param(param):
    def func_decorator(func):
        def func_caller(*args,**kwargs):
            kwargs[param] = kwargs[param] * kwargs[param]
            return func(*args,**kwargs)
    return func_caller
return func_decorator

Применяется к следующей функции:

@square_param('dividend')
def quotient(divisor=1,dividend=0):
    return dividend/divisor

Это будет работать, если дивиденд вызывается как аргумент ключевого слова, например:

>>> quotient(dividend=2)
4

Однако, если он задан в качестве позиционного аргумента, это потерпит неудачу.

>>> quotient(3,4)
TypeError: quotient() got multiple values for keyword argument 'dividend'

С помощью Python 3 я мог бы решить эту проблему, указав для параметра всегда заданное в качестве ключевого слова :

@square_param('dividend')
def quotient(divisor=1,*,dividend=0):
    return dividend/divisor

но я хотел бы поддержать Python 2, а также я хотел бы наложить как можно меньше ограничений на функцию.

Есть ли способ, которым я могу исправить это поведение в моем декораторе?

Ответы [ 2 ]

3 голосов
/ 05 июля 2011

Во-первых, ваш square_param декоратор не работает, потому что он не возвращает функции. Это должно быть:

def square_param(param):
    def func_decorator(func):
        def func_caller(*args,**kwargs):
            kwargs[param] = kwargs[param] * kwargs[param]
            return func(*args,**kwargs)
        return func_caller
    return func_decorator

Теперь я принял совет @ Дирка и заглянул в модуль inspect. Это можно сделать, проверив сначала, является ли параметр одним из позиционных аргументов функции, а затем, если указан этот позиционный аргумент, а затем изменив позицию этого аргумента. Вам также необходимо убедиться, что вы изменяете kwargs только в том случае, если параметр был указан в качестве аргумента ключевого слова.

import inspect

def square_param(param):
    def func_decorator(func):
        def func_caller(*args,**kwargs):
            funparams = inspect.getargspec(func).args
            if param in funparams:
                i = funparams.index(param)
                if len(args) > i:
                    args = list(args)   # Make mutable
                    args[i] = args[i] * args[i]
            if param in kwargs:
                kwargs[param] = kwargs[param] * kwargs[param]
            return func(*args,**kwargs)
        return func_caller
    return func_decorator
2 голосов
/ 05 июля 2011

даже без использования Inspect мы можем получить параметры функции

>>> func = lambda x, y, args: (x, y, {})
>>> func.func_code.co_argcount
3
>>> func.func_code.co_varnames
('x', 'y', 'args')
...