有没有办法在每次调用Python中的函数时进行一些预处理/后处理

问题描述 投票:0回答:2

我需要对从包(包是共享对象文件)导入的函数进行多次调用。但是,每次我从这个包中调用函数时,我都需要执行一些预处理/后处理步骤。像这样的东西:

import xyz

prepare()
xyz.foo(<args>)
done()

prepare()
xyz.bar(<args>)
done()

prepare()
xyz.foobar()
done()

有什么方法可以让 python 在从

prepare()
模块调用函数之前始终调用
xyz
。并在通话完成后调用
done()
? 在我的整个代码中编写
prepare
done
看起来多余且混乱。感谢您的帮助!

python python-3.x
2个回答
4
投票

这通常是通过上下文管理器完成的。

import contextlib

@contextlib.contextmanager
def preparation():
    prepare()
    yield
    done()

with preparation():
    xyz.foo(<args>)

with preparation():
    xyz.bar(<args>)

with preparation():
    xyz.foobar()

preparation
定义了一个返回上下文管理器的函数。
with
语句的工作原理是调用上下文管理器的
__enter__
方法,然后执行主体,然后确保在继续之前调用上下文管理器的
__exit__
方法(无论是由于引发异常还是主体正常完成) .

contextlib.contextmanager
提供了一种使用生成器函数定义上下文管理器的简单方法,而不是让您使用显式
__enter__
__exit__
方法定义类。


您提到特定模块中的每个功能都需要它。如果没有有关模块的确切详细信息,这可能不完全正确,但您也许可以在此基础上进行构建。

class XYZWrapper:
    def __getattr__(self, name):
        # Intentionally let an AttributeError propagate upwards
        f = getattr(xyz, name)
        def _(self, *args, **kwargs):
            prepare()
            return f(*args, **kwargs)
            done()
        setattr(XYZWrapper, name, _)
        return _

prepared = XYZWrapper()

prepared.foo(<args>)
prepared.bar(<args>)
prepared.foobar()
        
        

简而言之,对

XYZWrapper
实例的任何属性访问都会尝试在
xyz
模块上查找相同的属性,如果成功,则定义一个包装器,根据需要调用
prepare()
done()
并修补
XYZWrapper
带有新包装的实例。


2
投票

为了扩展 @chepner 的优秀答案,您可以定义自己的类并使用其

__getattr__
函数来创建一个函数,该函数将实际模块的函数与您的预处理和后处理函数包装起来:

import typing
import xyz

def XYZWrapper():
    def __init__(self, pre, post):
        self.pre = pre
        self.post = post

    def __getattr__(self, a):
        func = getattr(xyz, a)
        if isinstance(func, typing.Callable):
            def wrapper(*args, **kwargs):
                self.pre()
                func(*args, **kwargs)
                self.post()
            return wrapper
        raise TypeError(f"'{type(func)}' object is not callable")

要使用它,请执行以下操作

xyz = XYZWrapper(prepare, done)
xyz.foo(<args>)
...

请注意,如果您想覆盖

xyz
变量,则需要将包装类放在单独的文件中。

© www.soinside.com 2019 - 2024. All rights reserved.