Py学习  »  Python

每次我在python中调用一个函数时,有没有办法进行一些前处理/后处理

backstreetrover • 3 年前 • 1734 次点击  

我需要对从包(包是一个共享对象文件)导入的函数进行多次调用。然而,每次我从这个包调用函数时,我都需要做一些预处理/后处理步骤。比如:

import xyz

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

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

prepare()
xyz.foobar()
done()

有什么方法可以让python总是调用 prepare() 在调用函数之前 xyz 单元也可以调用 done() 通话结束后? 写 prepare done 在我看来,整个代码都是多余和混乱的。谢谢你的帮助!

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/127918
 
1734 次点击  
文章 [ 2 ]  |  最新文章 3 年前
Pranav Hosangadi
Reply   •   1 楼
Pranav Hosangadi    3 年前

延长 @chepner's excellent answer ,您可以定义自己的类并使用其 __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 变量,则需要将包装器类放在单独的文件中。

chepner
Reply   •   2 楼
chepner    3 年前

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

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 提供了一种使用生成器函数定义上下文管理器的简单方法,而不是使用显式 __进入__ __退出__ 方法。


您提到,对于特定模块中的每个函数,都需要这个。如果没有关于该模块的确切细节,这可能并不完全正确,但您可能可以在其基础上进一步扩展。

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 实例使用新包装器。