2013-06-15 2 views
5
from functools import wraps 
def logged(func): 
    @wraps(func) 
    def with_logging(*args, **kwargs): 
     print func.__name__ + " was called" 
     return func(*args, **kwargs) 
    return with_logging 

@logged 
def f(x): 
    """does some math""" 
    return x + x * x 

print f.__name__ # prints 'f' 
print f.__doc__ # prints 'does some math' 

Учитывая этот пример кода, как бы я мог сделать @logged(variable)?Python decorator необязательный аргумент

Я попробовал этот

from functools import wraps 
def logged(func): 
    def outer(var): 
     @wraps(func) 
     def with_logging(*args, **kwargs): 
      print func.__name__ + " was called" 
      return func(*args, **kwargs) 
     return with_logging 
    return outer 

Я надеялся выполнить так: вход (FUNC) (session_variable)

Но не работает. Любая идея? Я хочу иметь возможность делать @logged и @logged (var) (или даже @logged (var1, var2)) Спасибо.

ответ

11

Хитрость здесь, вы должны вникать, что вы получаете:

def logged(*setting_args, **setting_kwargs): 
    no_args = False 
    if len(setting_args) == 1 \ 
     and not setting_kwargs \ 
     and callable(setting_args[0]): 
     # We were called without args 
     func = setting_args[0] 
     no_args = True 

    def outer(func): 
     @wraps(func) 
     def with_logging(*args, **kwargs): 
      print "{} was called".format(func.__name__) 
      print "Setting args are: {}".format(setting_args) 
      print "Setting keyword args are: {}".format(setting_kwargs) 
      return func(*args, **kwargs) 
     return with_logging 

    if no_args: 
     return outer(func) 
    else: 
     return outer 

Это будет работать с любым из следующих:

# No arguments 
@logged 
def some_function(x): 
    pass 

# One or more arguments 
@logged(1, 2, 3) 
def some_function(x): 
    pass 

# One or more keyword arguments 
@logged(key=1, another_key=2) 
def some_function(x): 
    pass 

# A mix of the two 
@logged(1, 2, key=3) 
def some_function(x): 
    pass 

Это не работать, если это вызванный только одним вызываемым аргументом:

# This will break. 
@logged(lambda: "Just for fun") 
def some_function(x): 
    pass 

Существует n o способ рассказать разницу между одной настраиваемой настройкой и вызовом no-arg декоратора. Тем не менее, вы можете передать ARG мусора ключевого слова, чтобы обойти даже, что если вам нужно:

# This gets around the above limitation 
@logged(lambda: "Just for fun", ignored=True) 
def some_function(x): 
    pass 
+0

спасибо. Я многому научился. – user423455

+0

Отлично, спасибо. Это помогло мне реализовать декоратор 'required_params()'. – Jason

0

поставил def outer(var) наружу, то есть

def outer(var): 
    def logged(func): 
     ... 

, а затем использовать @outer(somevar) для вашей функции, это будет работать.

0

Существует небольшая ошибка в коде вы пробовали. Вместо создания вложенных функций с аргументами как func>var>*args, **kwargs, заказ должен быть var>func>*args, **kwargs.

Ниже приведен фрагмент кода, достаточный для вашего требования.

from functools import wraps 

def logged(var=None): 
    def outer(func): 
     @wraps(func) 
     def with_logging(*args, **kwargs): 
      print func.__name__ + " was called" 
      return func(*args, **kwargs) 
     return with_logging 
    return outer 

Вы можете назвать этот декоратор, как:

@logged 
def func1(): 
    ... 

ИЛИ

@logged(xyz) 
def func2(): 
    ... 

Чтобы узнать больше о том, как работают декораторы, смотрите статью Decorators with optional arguments.

0

Возможное альтернативное решение Шон Виейра может быть:

from functools import wraps 
import inspect 


def decorator_defaults(**defined_defaults): 
    def decorator(f): 
     args_names = inspect.getargspec(f)[0] 

     def wrapper(*new_args, **new_kwargs): 
      defaults = dict(defined_defaults, **new_kwargs) 
      if len(new_args) == 0: 
       return f(**defaults) 
      elif len(new_args) == 1 and callable(new_args[0]): 
       return f(**defaults)(new_args[0]) 
      else: 
       too_many_args = False 
       if len(new_args) > len(args_names): 
        too_many_args = True 
       else: 
        for i in range(len(new_args)): 
         arg = new_args[i] 
         arg_name = args_names[i] 
         defaults[arg_name] = arg 
       if len(defaults) > len(args_names): 
        too_many_args = True 
       if not too_many_args: 
        final_defaults = [] 
        for name in args_names: 
         final_defaults.append(defaults[name]) 
        return f(*final_defaults) 
       if too_many_args: 
        raise TypeError("{0}() takes {1} argument(s) " 
            "but {2} were given". 
            format(f.__name__, 
              len(args_names), 
              len(defaults))) 
     return wrapper 
    return decorator 


@decorator_defaults(start_val="-=[", end_val="]=-") 
def my_text_decorator(start_val, end_val): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      return "".join([f.__name__, ' ', start_val, 
          f(*args, **kwargs), end_val]) 
     return wrapper 
    return decorator 


@decorator_defaults(end_val="]=-") 
def my_text_decorator2(start_val, end_val): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      return "".join([f.__name__, ' ', start_val, 
          f(*args, **kwargs), end_val]) 
     return wrapper 
    return decorator 


@my_text_decorator 
def func1a(value): 
    return value 


@my_text_decorator() 
def func2a(value): 
    return value 


@my_text_decorator2("-=[") 
def func2b(value): 
    return value 


@my_text_decorator(end_val=" ...") 
def func3a(value): 
    return value 


@my_text_decorator2("-=[", end_val=" ...") 
def func3b(value): 
    return value 


@my_text_decorator("|> ", " <|") 
def func4a(value): 
    return value 


@my_text_decorator2("|> ", " <|") 
def func4b(value): 
    return value 


@my_text_decorator(end_val=" ...", start_val="|> ") 
def func5a(value): 
    return value 


@my_text_decorator2("|> ", end_val=" ...") 
def func5b(value): 
    return value 


print(func1a('My sample text')) # func1a -=[My sample text]=- 
print(func2a('My sample text')) # func2a -=[My sample text]=- 
print(func2b('My sample text')) # func2b -=[My sample text]=- 
print(func3a('My sample text')) # func3a -=[My sample text ... 
print(func3b('My sample text')) # func3b -=[My sample text ... 
print(func4a('My sample text')) # func4a |> My sample text <| 
print(func4b('My sample text')) # func4b |> My sample text <| 
print(func5a('My sample text')) # func5a |> My sample text ... 
print(func5b('My sample text')) # func5b |> My sample text ... 

Примечание: оно имеет тот же недостаток, где вы не можете передать 1 аргумент функции для декоратора, но если вы хотите, чтобы эта функциональность на нескольких декораторов вы можете уклониться от шаблона кода.

Смежные вопросы