You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:46:47