Python2/3兼容下Tornado协程关键字参数检查方案问询
在Python 2中检查Tornado协程的关键字参数
我最近遇到了这个问题——要检查Tornado协程是否包含特定的关键字参数,Python 3里用inspect.signature很轻松,但Python 2没有这个方法,inspect.getargspec又没法直接处理被协程装饰后的函数。折腾了一番,找到了几个可行的替代方案,分享给大家:
先看测试场景的代码:
import inspect import tornado.gen class Foo(object): def a(self, x, y=None): pass @tornado.gen.coroutine def coro_func(self, foo, bar=None, baz=123): raise tornado.gen.Return("done")
方案1:访问协程包装的原始函数
Tornado的gen.coroutine装饰器会把原始函数保存在__wrapped__属性里(Python 2.7及以上版本支持这个属性),我们可以先拿到原始函数,再用inspect.getargspec分析它的参数信息。
具体实现代码:
def has_kwarg(coro_func, kwarg_name): # 获取被协程装饰器包裹的原始函数 original_func = coro_func.__wrapped__ argspec = inspect.getargspec(original_func) # 带默认值的参数就是函数的关键字参数,对应argspec.defaults里的内容 if argspec.defaults: # 带默认值的参数是参数列表的最后N个,N等于defaults的长度 kwarg_names = argspec.args[-len(argspec.defaults):] return kwarg_name in kwarg_names # 如果没有默认参数,自然不存在关键字参数 return False # 测试 print(has_kwarg(Foo.coro_func, "bar")) # 输出 True print(has_kwarg(Foo.coro_func, "qux")) # 输出 False
注意点
- 如果协程被多个装饰器嵌套包裹,可能需要递归查找
__wrapped__属性直到找到最原始的函数,但一般Tornado协程装饰器直接包裹原始函数,这种情况很少见。 inspect.getargspec虽然有局限(比如不支持Python 3的Keyword-only参数,但Python 2本身也没有这个特性),但对于检查普通关键字参数的需求完全够用。
方案2:解析函数源码(备用方案)
如果因为某些原因无法使用__wrapped__属性(比如装饰器未设置该属性),可以尝试解析函数源码来提取参数信息。这个方法比较“hack”,但在无法获取原始函数时能应急。
具体实现代码:
import re def has_kwarg_via_source(coro_func, kwarg_name): try: # 获取函数的源码文本 source = inspect.getsource(coro_func) except IOError: # 如果无法获取源码(比如是编译后的pyc文件),直接返回False return False # 用正则匹配函数定义行的参数部分 match = re.search(r'def\s+\w+\s*\((.*?)\):', source, re.DOTALL) if not match: return False args_part = match.group(1) # 分割参数,处理可能的换行和空格 args = [arg.strip() for arg in args_part.split(',') if arg.strip()] # 遍历参数,检查带默认值的参数是否包含目标名称 for arg in args: if '=' in arg: name = arg.split('=')[0].strip() if name == kwarg_name: return True return False # 测试 print(has_kwarg_via_source(Foo.coro_func, "bar")) # 输出 True print(has_kwarg_via_source(Foo.coro_func, "qux")) # 输出 False
注意点
- 这个方法依赖于函数源码的可读性,如果源码被混淆、压缩或者无法访问(比如第三方库的编译文件),就会失效。
- 正则表达式可能无法处理极端复杂的参数定义(比如带注释的参数、嵌套括号),但对于常规的函数定义完全没问题。
总结
优先使用方案1,它更可靠、效率更高;只有当方案1不可行时,再考虑方案2作为备用。
内容的提问来源于stack exchange,提问作者MRocklin
相关产品推荐
相关产品推荐

