英文:
Nested decorators in Python defining simulation methods
问题
以下是你提供的代码的中文翻译:
让我们假设我们有三种模拟方法:
```python
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
这样,我们可以通过以下方式使用simulation
函数调用特定的方法:
@method3
def simulation(simulation_method):
# 需要进行模拟的一些计算的实现
# 但作为一个玩具模型,我们只是打印以下语句:
print(f"使用{simulation_method}方法运行模拟")
这将产生以下输出:
"使用method3方法运行模拟"
现在,我想定义一个名为MultiSimulation
的装饰器,它会重复调用模拟函数,同时使用给定的模拟方法,具体用法如下:
@MultiSimulation
@method1
@method2
@method3
def simulation(simulation_method):
print(f"使用{simulation_method}方法运行模拟")
这应该生成以下输出:
"使用method1方法运行模拟"
"使用method2方法运行模拟"
"使用method3方法运行模拟"
我卡在了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
但我没有得到任何输出。
英文:
Let's say we have three simulation methods:
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
such that we can call a simulation
function with a specific method via
@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")
which yields the output
"Running simulation with method3 method"
I now want to define a decorator called MultiSimulation
that repeatedly calls the simulation function while using the given simulation methods with the following syntax:
@MultiSimulation
@method1
@method2
@method3
def simulation(simulation_method):
print(f"Running simulation with {simulation_method} method")
This should give the output:
"Running simulation with method1 method"
"Running simulation with method2 method"
"Running simulation with method3 method"
I am stuck with the definition of MultiSimulation and would be glad to get some help here. Thanks!
I tried different variations such as
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
but I don't get any output.
答案1
得分: 3
当你叠加装饰器时,你应该注意它们是从底部向顶部装饰的。这意味着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
英文:
When you're stacking decorators you should note that they are decorated from bottom to the top. This means method3
decorates the simulation
and method2
decorates "this decorated function" not the simulation
itself. But as you've shown in your question you need to "repeat" the function with different decorators. Of course there are ways to do so but I would rather not doing it that way.
You can instead pass your simulation methods to MultiSimulation
like:
@MultiSimulation(method1, method2, method3)
Here is an implementation:
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()
output:
Running simulation with method1 method
Running simulation with method2 method
Running simulation with method3 method
答案2
得分: 3
要求重新设计装饰器以支持装饰器叠加
重新设计后,您可以获得以下效果:
@MultiSimulation
@method1
@method2
@method3
def simulation(simulation_method):
print(f"使用 {simulation_method} 方法运行模拟")
return simulation_method
print(simulation())
# 使用 method1 方法运行模拟
# 使用 method2 方法运行模拟
# 使用 method3 方法运行模拟
# ['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
通过重新设计这些装饰器,您可以在需要时使用原始的方式,同时获取不同模拟的返回值。
英文:
Decorator rework required to keep decorator stacking
With the rework, here what you can get:
@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']
You need to update your decorators this way:
def method1(func):
def wrapper1(*args, simulation_method="method1", **kwargs):
return func(*args, simulation_method=simulation_method, **kwargs)
return wrapper1
And you need this decorator:
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
With this rework of decorators, it's possible to use your original style while getting the return values of the different simulation if necessary.
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']
答案3
得分: 0
我不喜欢使用任意数量的装饰器,因为这往往会变得混乱,而且顺序很重要。在这里,我将使用一个支持多个装饰器的基于类的装饰器,其构造函数如下:
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"
英文:
I dislike using an arbitrary number of decorators, since it tends to get messy and order matters when placing them. Here, I would use a class-based decorator that supports multiple decorators in it's constructor:
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 this code yields:
"Running simulation with method1 method"
"Running simulation with method2 method"
"Running simulation with method3 method"
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论