python3 - 装饰器函数:赋值前引用的变量

Posted

技术标签:

【中文标题】python3 - 装饰器函数:赋值前引用的变量【英文标题】:python3 - decorator function: Variable referenced before assignment 【发布时间】:2019-03-17 20:50:16 【问题描述】:

我正在开发 gRPC 微服务。

因为每个方法都必须先从 request 参数加载一个 JSON 字符串 - 然后最后再次转储它,我想在类的方法上使用装饰器,以便方法本身除了 @ 987654323@ 只包含下面的... more stuff to do ...

class VPPSetupService(SetupService_pb2_grpc.VPPSetupServiceServicer):
...
   def method(self, request, context):
        request = json.loads(request.context)
        request = request["request"]
        header = request["headers"]
        ... more stuff to do ...
        response = json.dumps(response)
        return SetupService_pb2.JsonContextResponse(context=response)

所以我写了一个装饰器函数。 但是,我找不到这个错误的解决方案:

request = json.loads(request.context)

UnboundLocalError:分配前引用了局部变量“请求”

错误是由这个产生的:(仅作为示例,真实的东西要复杂一些)

from functools import wraps

def json_and_error(func):
        @wraps(func)
        def args_wrapper(*args, **kwargs):           # Problem: This
            request = json.loads(request.context)  <<# variable is referenced
            request = request["request"]             # before assignment
            header = request["headers"]
            func(*args, **kwargs)
            return func(*args, **kwargs)
        return args_wrapper

class VPPSetupService(SetupService_pb2_grpc.VPPSetupServiceServicer):
    ...
       @json_and_error
       def method(self, request, context):
            ... more stuff to do ...
            return SetupService_pb2.JsonContextResponse(context=response)

我尝试改用request = json.loads(args[1].context)。但后来我得到这个错误:

如果 request["source_machine"] 和 request["dest_machine"]: TypeError:

'JsonContextRequest' 对象不可下标

作为request 参数给出的输入是&lt;class 'SetupService_pb2.JsonContextRequest'&gt; 类型的对象,请求中的JSON 字符串可以通过request.context 属性访问。

我认为问题与如何调用装饰器函数有关。我想如果它是在类方法的运行时调用的,那么变量 request 应该已经被分配了 request 对象。

但也许我在这里完全错了。那你会怎么解决呢?

【问题讨论】:

【参考方案1】:

您的代码中几乎没有错误:

def args_wrapper(*args, **kwargs):
    request = json.loads(request.context)

您正在使用request.context,而request 变量未定义(只有在json.loads(request.context) 执行后才会定义)。您尝试使用 request = json.loads(args[1].context) 修复它,但在包装函数中得到了 JsonContextRequest object is not subscriptable。仔细看看这条线:

return func(*args, **kwargs)

您的装饰器返回使用相同参数调用的包装函数,因此不使用此代码结果,您不将请求和标头传递给包装函数:

request = json.loads(request.context)
request = request["request"]
header = request["headers"]

你也不应该调用包装函数:

func(*args, **kwargs)

我猜你想做这样的事情:

def json_and_error(func):
    def args_wrapper(this, request):
        context = request.context
        request = json.loads(context)
        request = request["request"]

        return func(this, request, context)

    return args_wrapper

【讨论】:

谢谢你,Ingvar,这对我来说很有意义。为什么我要调用函数并返回它。我会试一试,然后告诉你。 2个问题:为什么传递*args和**kwargs是不够的?我认为这会传递所有位置和可选参数?并且:我可以使用 self 代替 this,并遵守约定,对吗? 1) 当你不知道确切的函数/方法参数计数时,传递 args 和 kwargs 更合理,它也会降低代码的可读性(例如args[1].context) - 这里的args[1] 是什么?) 2) 我使用this 是因为我想提一下args_wrapper 不是一种方法,而是一种功能。 self 通常是方法中的第一个参数。无论如何,如果你愿意,你可以在这里使用self

以上是关于python3 - 装饰器函数:赋值前引用的变量的主要内容,如果未能解决你的问题,请参考以下文章

python闭包函数&装饰器

python3装饰器的简单解释

python3 装饰器

Python装饰器和闭包函数

函数之装饰器

Python3 装饰器