社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

backstreetrover • 3 年前 • 1599 次点击  

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

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
 
1599 次点击  
文章 [ 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 实例使用新包装器。