Допустим, у нас есть три метода моделирования:
def method1(func):
def wrapper(*args, **kwargs):
#Implementation of some simulator backend
#but as a toy model we just pass a string here
return func(*args, simulation_method='method1', **kwargs)
return wrapper
def method2(func):
def wrapper(*args, **kwargs):
#Implementation of some simulator backend
#but as a toy model we just pass a string here
return func(*args, simulation_method='method2', **kwargs)
return wrapper
def method3(func):
def wrapper(*args, **kwargs):
#Implementation of some simulator backend
#but as a toy model we just pass a string here
return func(*args, simulation_method='method3', **kwargs)
return wrapper
Так что мы можем вызвать функцию simulation с помощью определенного метода через
@method3
def simulation(simulation_method):
#Implementation of some computation that needs to be simulated
#but as a toy model we just print the following statement:
print(f"Running simulation with {simulation_method} method")
Что дает результат
"Running simulation with method3 method"
Теперь я хочу определить декоратор с именем MultiSimulation, который многократно вызывает функцию моделирования при использовании заданных методов моделирования со следующим синтаксисом:
@MultiSimulation
@method1
@method2
@method3
def simulation(simulation_method):
print(f"Running simulation with {simulation_method} method")
Это должно дать результат:
"Running simulation with method1 method"
"Running simulation with method2 method"
"Running simulation with method3 method"
Я застрял с определением MultiSimulation и был бы рад получить здесь помощь. Спасибо!
Я пробовал разные варианты, такие как
def MultiSimulation(func):
def repeated_simulation(*args, **kwargs):
simulation_methods = []
if hasattr(func, '__wrapped__'):
simulation_methods = func.__wrapped__.simulation_methods
result = None
for simulation_method in simulation_methods:
kwargs['simulation_method'] = simulation_method
result = func(*args, **kwargs)
return result
repeated_simulation.simulation_methods = []
repeated_simulation.__wrapped__ = func
return repeated_simulation
Но я не получаю никакого вывода.
Когда вы укладываете декораторы, обратите внимание, что они декорируются снизу вверх. Это означает, что method3 украшает simulation, а method2 украшает «эту декорированную функцию», а не саму simulation. Но, как вы показали в своем вопросе, вам нужно «повторить» функцию с разными декораторами. Конечно, есть способы сделать это, но я бы предпочел не делать этого таким образом.
Вместо этого вы можете передать свои методы моделирования MultiSimulation, например:
@MultiSimulation(method1, method2, method3)
Вот реализация:
def method1(func):
def wrapper(*args, **kwargs):
return func(*args, simulation_method = "method1", **kwargs)
return wrapper
def method2(func):
def wrapper(*args, **kwargs):
return func(*args, simulation_method = "method2", **kwargs)
return wrapper
def method3(func):
def wrapper(*args, **kwargs):
return func(*args, simulation_method = "method3", **kwargs)
return wrapper
def MultiSimulation(*simulation_methods):
def decorator(fn):
def inner(*args, **kwargs):
return [m(fn)(*args, **kwargs) for m in simulation_methods]
return inner
return decorator
@MultiSimulation(method1, method2, method3)
def simulation(simulation_method):
print(f"Running simulation with {simulation_method} method")
simulation()
Выход:
Running simulation with method1 method
Running simulation with method2 method
Running simulation with method3 method
Я предпочитаю этот стиль стеку декоратора.
Спасибо за этот ответ. Я должен согласиться с Дорианом Турба, что этот подход лучше, чем то, что я имел в виду изначально! Я новичок в использовании декораторов и узнал от вас кое-что новое.
@LinLin Добавлено объяснение, почему вам нужно что-то подобное.
Мне не нравится использовать произвольное количество декораторов, так как это приводит к беспорядку и порядку при их размещении. Здесь я бы использовал декоратор на основе классов, который поддерживает несколько декораторов в своем конструкторе:
class MultiSimulation:
def __init__(self, *methods):
self.methods = methods
def __call__(self, func):
def wrapper(*args, **kwargs):
return [method(func)(*args, **kwargs) for method in self.methods]
return wrapper
def method1(func):
def wrapper(*args, **kwargs):
return func(*args, simulation_method='method1', **kwargs)
return wrapper
def method2(func):
def wrapper(*args, **kwargs):
return func(*args, simulation_method='method2', **kwargs)
return wrapper
def method3(func):
def wrapper(*args, **kwargs):
return func(*args, simulation_method='method3', **kwargs)
return wrapper
@MultiSimulation(method1, method2, method3)
def simulation(simulation_method):
print(f"Running simulation with {simulation_method} method")
simulation()
Запуск этого кода дает:
"Running simulation with method1 method"
"Running simulation with method2 method"
"Running simulation with method3 method"
Спасибо. Я очень ценю ваше решение, которое приводит к тому же синтаксису, что и в ответе С.Б. Построение на основе классов выглядит очень интересно! Я также предпочитаю этот подход тому, что я изначально имел в виду.
С переработкой вот что вы можете получить:
@MultiSimulation
@method1
@method2
@method3
def simulation(simulation_method):
print(f"Running simulation with {simulation_method} method")
return simulation_method
print(simulation())
# Running simulation with method1 method
# Running simulation with method2 method
# Running simulation with method3 method
# ['method1', 'method2', 'method3']
Вам нужно обновить декораторы следующим образом:
def method1(func):
def wrapper1(*args, simulation_method = "method1", **kwargs):
return func(*args, simulation_method=simulation_method, **kwargs)
return wrapper1
И вам нужен этот декоратор:
def MultiSimulation(func):
def repeated_simulation(*args, **kwargs):
tmp_fct = func
results = []
while tmp_fct:
try:
results.append(tmp_fct(*args, **kwargs))
except TypeError:
pass
try:
tmp_fct = tmp_fct.__closure__[0].cell_contents
except TypeError:
break
return results
return repeated_simulation
Благодаря этой переработке декораторов можно использовать исходный стиль, получая при необходимости возвращаемые значения другой симуляции.
def method1(func):
def wrapper1(*args, simulation_method = "method1", **kwargs):
return func(*args, simulation_method=simulation_method, **kwargs)
return wrapper1
def method2(func):
def wrapper2(*args, simulation_method = "method2", **kwargs):
return func(*args, simulation_method=simulation_method, **kwargs)
return wrapper2
def method3(func):
def wrapper3(*args, simulation_method = "method3", **kwargs):
return func(*args, simulation_method=simulation_method, **kwargs)
return wrapper3
def MultiSimulation(func):
def repeated_simulation(*args, **kwargs):
tmp_fct = func
results = []
while tmp_fct:
try:
results.append(tmp_fct(*args, **kwargs))
except TypeError:
pass
try:
tmp_fct = tmp_fct.__closure__[0].cell_contents
except TypeError:
break
return results
return repeated_simulation
@MultiSimulation
@method1
@method2
@method3
def simulation(simulation_method):
print(f"Running simulation with {simulation_method} method")
return simulation_method
print(simulation())
# Running simulation with method1 method
# Running simulation with method2 method
# Running simulation with method3 method
# ['method1', 'method2', 'method3']
Вы согласны сменить декораторов?