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

jgzswidk  于 2023-02-15  发布在  Python
关注(0)|答案(3)|浏览(110)

假设我们有三种模拟方法:

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"

我被多重模拟的定义卡住了,很高兴能在这里得到一些帮助。谢谢!
我尝试了不同的变体,例如

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

但没有任何输出。

4ioopgfo

4ioopgfo1#

装饰工需要返工,以保持装饰工堆叠

通过返工,您可以获得以下结果:

@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']
lnlaulya

lnlaulya2#

当你堆叠装饰器的时候,你应该注意到它们是从下往上装饰的。这意味着method3装饰simulationmethod2装饰“this decored function”而不是simulation本身。但是正如你在问题中所展示的,你需要用不同的装饰器**“repeat”**这个函数。当然有办法这样做,但我宁愿不这样做。
您可以将模拟方法传递给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
jljoyd4f

jljoyd4f3#

我不喜欢使用任意数量的装饰器,因为在放置它们时会变得混乱和有序。这里,我将使用一个基于类的装饰器,它在构造函数中支持多个装饰器:

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"

相关问题