有没有一种方法可以在每次调用python中的函数时进行一些预处理/后处理



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

import xyz
prepare()
xyz.foo(<args>)
done()
prepare()
xyz.bar(<args>)
done()
prepare()
xyz.foobar()
done()

在从xyz模块调用函数之前,有没有办法让python始终调用prepare()。并且还要在调用完成后调用done()?在我的整个代码中编写preparedone似乎是多余和混乱的。感谢你的帮助!

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

import contextlib
@contextlib.contextmanager
def with_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实例。

为了扩展@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变量,则需要将包装器类放在一个单独的文件中。

最新更新