嵌套装饰器在Python中定义模拟方法

huangapple go评论63阅读模式
英文:

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"

huangapple
  • 本文由 发表于 2023年2月14日 21:49:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75448795.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定