如何编写一个装饰器来在上下文管理器中包装一些东西,它需要参数?

Posted

技术标签:

【中文标题】如何编写一个装饰器来在上下文管理器中包装一些东西,它需要参数?【英文标题】:How do I write a decorator to wrap something in a context manager, that takes parameters? 【发布时间】:2015-08-09 21:58:58 【问题描述】:

我见过How to use a context manager inside a decorator and how to pass an object created in decorator to decorated function 和python decorators with parameters,我正在尝试将两者结合起来......但我很难理解它。

如果可能的话,我更愿意使用 func 工具 @wrap 装饰器来执行此操作,因为我知道是否会保留文档字符串。

我想做的是这样的:

def pyro_opener(func,service,database,port,secret_key):
    def wrapper(params):
        with Pyro4.Proxy("PYRO:"+service+"@"+database+":"+port) as obj:
            obj.set_secret_key(secret_key)
            return obj.func(params)
    return wrapper


@pyro_opener(output_service, employee_db,port=9876,secret_key="h3llow0rld")
def get_employee_names(salary):
    return obj.return_employee_names(salary)  # obj is clearly not in scope here
                                              # but what else can I do?


get_employee_names(25000)

>>>> Bob, Jane, Mary

我不认为这种方式有效,return_employee_names 方法在连接另一端的服务上。我应该只返回函数调用吗?如果是这样,我该如何传递参数呢?

【问题讨论】:

【参考方案1】:

您将通过with ... as 绑定到的对象传递给包装函数;该函数必须接受这样的论点。

这类似于方法的工作方式;它们只是传入了一个额外的第一个参数 (self) 的函数:

def pyro_opener(service, database, port, secret_key):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            with Pyro4.Proxy("PYRO:@:".format(service, database, port)) as obj:
                obj.set_secret_key(secret_key)
                return func(obj, *args, **kw)
        return wrapper
    retutrn decorator

@pyro_opener(output_service, employee_db, port=9876, secret_key="h3llow0rld")
def get_employee_names(obj, salary):
    return obj.return_employee_names(salary)

请注意,我必须在 pyro_opener() 中添加另一个嵌套函数以使其成为合适的装饰器工厂。

【讨论】:

那么当我调用get_employee_names 时,我从哪里获得 obj 参数?另外,我认为使用@wraps 意味着我不需要额外的装饰性......? @Pureferret:仔细看看我的get_employee_names() 版本; obj 参数是显式functools.wraps() 与装饰器工厂无关,与将元数据从包装函数复制到包装器无关,因此后者看起来更像原始。 @martjinpieters 我的意思是,当我想使用 `get_employee_names` 时,我需要提供 obj 参数(我需要提供薪水的行)。我不能像 get_employee_names(???,300000) 这样调用方法,但我应该为 ??? 输入什么? 你没有; decorator wrapper 传入该参数。就像在实例上调用方法一样,其中self 由 Python 为您提供。 仔细观察wrapper()如何调用func()

以上是关于如何编写一个装饰器来在上下文管理器中包装一些东西,它需要参数?的主要内容,如果未能解决你的问题,请参考以下文章

在 try / 中包装类方法,除了使用装饰器

如何创建一个装饰器来装饰生成器函数? [关闭]

使用属性装饰器来启动一个类

Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?

为啥 Python 中没有 @override 装饰器来帮助提高代码的可读性? [关闭]

你如何获得更广泛的上下文 HTML,在 jQuery 或 Cheerio js 中包装给定的选择器代码?