2013-08-02 2 views
0

У меня есть некоторые функции, которые при нормальных обстоятельствах вызываются с аргументами, предоставленными пользователем. Однако справедливо назвать некоторые из этих функций некоторыми рядами аргументов, которые определяются во время выполнения на основе некоторого состояния системы.Декоратор, который условно активирует другой декоратор?

Я хочу, чтобы у пользователя был , а необязательно поручил моей программе вызывать эти функции со всеми действительными вводами и возвращать результаты каждого вызова. Я думаю, что декоратор, который будет работать как активационный переключатель для функций, которые имеют другой декоратор, который указывает, какая серия аргументов для использования будет работать хорошо.

Кроме того, I требуется для сохранения сигнатуры функции и метаданных. Это жизненно важно для работы моей программы.

Это то, что я пробовал, но он не работает. Он основан на this example.

>>> from decorator import decorator 
>>> def the_list(): 
...  return ["foo", "bar", "baz"] 
... 
>>> import itertools 
>>> @decorator 
... def do_all(func): 
...  # this will do nothing (besides wrap in a tuple) unless func is decorated with @gets_arg_from 
...  if hasattr(func, 'get_from'): 
...   return tuple(func(*args) for args in itertools.product(*(list_fun() for list_fun in func.get_from))) 
...  else: 
...   return (func(),) 
... 
>>> def gets_arg_from(*list_funcs): 
...  # this will do nothing to func unless decorated with @do_all 
...  def gets_arg_from(func, *args, **kwargs): 
...   func.get_from = list_funcs 
...   return func(*args, **kwargs) 
...  return decorator(gets_arg_from) 
... 
>>> @gets_arg_from(the_list) 
... def print_func(word): 
...  # this will print its argument unless decorated with @do_all 
...  # at that point it will print every element returned by the_list() 
...  print word 
... 
>>> print_func("foo") 
foo 
>>> all = decorator(do_all, print_func) 
>>> all() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: print_func() takes exactly 1 argument (0 given) 
>>> print_func.get_from 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'get_from' 

Что я ожидал был:

>>> all() 
("foo", "bar", "baz") 

То, что я заметил, это не так:

  1. gets_arg_from не добавляет атрибут get_from к func.
  2. Кое-что обо мне, используя обозначение @gets_arg_from(the_list) не так. Он думает, что я пытаюсь передать два аргумента (но почему это будет проблема так или иначе?)

Что касается моей мотивации, я думаю, декораторов для этого, потому что есть буквально сотни этих процедур, их детали реализации (а также их функциональная корректность) подвержены частым изменениям, и я не хочу использовать inspect, чтобы рассуждать о том, что делать на основе их имен аргументов, и я не хочу жестко кодировать функциональность do_all для каждой функции для что имеет смысл. Методы класса могут работать, но для моей цели они семантически надуманны. Кроме того, ради других, которым, возможно, придется поддерживать мой код, я думаю, что проще попросить их применить декоратор и не беспокоиться об остальном, а не использовать определенные имена аргументов или разместить функцию в определенном классе или без разницы. Я понимаю, что этот вопрос может показаться странным, поэтому я решил, что эта сноска может помочь мне выглядеть меньше, чем сумасшедший.

+0

Что вы на самом деле делаете? Это похоже на модульное тестирование. Вы единичное тестирование? – user2357112

+0

@ user2357112 Нет, я не знаю. Я пытаюсь сделать своего рода «переключатель» для функций, которые имеют определенные свойства (т. Е. Определенный тип декоратора). – 2rs2ts

+0

Я не уверен, что я слежу за 100%, но моя первоначальная мысль заключалась в том, что вы можете работать с 'functools.partial' здесь ...? –

ответ

1

Разве это не то, что вы хотите?

import functools 
from itertools import product 

def the_list(): 
    return ["foo", "bar", "baz"] 


def do_all(func): 
    if hasattr(func, 'get_from'): 
     @functools.wraps(func) 
     def wrapper(*args, **kwargs): 
      return tuple(func(*args) for args in 
         product(*(lf() for lf in func.get_from))) 
     return wrapper 
    return func 

def gets_arg_from(*list_funcs): 
    def decorator(func): 
     func.get_from = list_funcs 
     return func 
    return decorator 

@gets_arg_from(the_list) 
def print_func(word): 
    return word 

print print_func('foo') 

all = do_all(print_func) 
print all() 

Edit: Объяснение

Эти два сегмента кода идентичны:

@deco 
def func(...): 
    some code 

такая же, как

func = deco(lambda ...: some code) 

@something просто синтаксический сахар для вызов функции и создание анонимной функции ...

Я объясню, что произошло в следующий мир шаг за шагом кода:

@gets_arg_from(the_list) 
def print_func(word): 
    return word 
  1. Первый Python создает Anonimous функцию, которая принимает параметр word и имеет корпус, который просто возвращает этот word (или делает то, что делает тело функции)

  2. Затем вызывается функция get_arg_from и передается the_list в качестве аргумента

  3. get_arg_from создает decorator функцию и returnes его

  4. decorator функция возвращается из get_arg_from называется (это синтаксический сахар вещь), проходящей в качестве аргумента func Anonimous функция, созданная в шаге 1.

  5. decorator просто присваивает list_funcs кортежа к атрибуту анонимной функции get_from и возвращает Anonimous функции

  6. Возвращаемое значение функции decorator присваивается переменной print_func

Подобный эффект может быть достигнут с:

def __anonimous(word): 
    return word 
__decorator = gets_arg_from(the_list) 
print_func = __decorator(__anonimous) 

Так в основном gets_arg_from не декоратор это функция, которая возвращает декоратора ,

do_all с другой стороны является декоратором, он принимает функцию в качестве аргумента, и возвращает либо исходную функцию (если функция не имеет атрибут get_from) или wrapper функцию, которая заменяет исходную функцию (если у него есть атрибут get_from).

Другие примеры: here.

+0

Да, похоже. Почему подпись сохраняется при использовании декоратора '@ get_arg_from'? Очевидно, я не знаю достаточно, чтобы понять, почему, но мне бы хотелось объяснить это, потому что я думал, что украшение немедленно уничтожило аргумент arg. – 2rs2ts

+0

Что вы подразумеваете под "signature get saved"? –

+0

'inspect.getargspec (print_func)' дает мне 'ArgSpec (args = ['word'], varargs = None, keywords = None, defaults = None)'. Я ожидал, что он даст мне спецификацию arg из 'gets_arg_from' ... очевидно, я смущен тем, как декораторы работают на Python. – 2rs2ts

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

  • Нет связанных вопросов^_^