
本文介绍如何使用 Python 的 inspect 模块准确获取任意类(包括未知类型)的 __init__ 方法所声明的形式参数个数,支持默认参数识别,并提供健壮的实现方式与关键注意事项。
本文介绍如何使用 python 的 `inspect` 模块准确获取任意类(包括未知类型)的 `__init__` 方法所声明的形式参数个数,支持默认参数识别,并提供健壮的实现方式与关键注意事项。
在 Python 中,若已知一个类的类型(例如存储在变量 a_type 中),我们常需动态判断其构造函数 __init__ 接受多少个非绑定、显式声明的位置/关键字参数(即不计 self)。这在泛型工厂、序列化框架、依赖注入或自动化测试等场景中非常实用。
核心方案是使用标准库 inspect.signature() —— 它能安全提取可调用对象的签名信息。注意:应作用于类本身(而非 a_type() 或 a_type.__init__),因为 inspect.signature(a_type) 会自动解析其 __init__ 方法(若未自定义,则回退至 object.__init__)。
以下是一个健壮、可直接复用的工具函数:
import inspect
def nb_args(cls):
"""
返回类实例化时 __init__ 方法声明的形参个数(不含 self)。
对于无自定义 __init__ 的类,返回 0(因 object.__init__ 仅接受 self)。
"""
try:
sig = inspect.signature(cls)
params = list(sig.parameters.values())
# 过滤掉第一个参数(通常是 'self'),但兼容无 self 的特殊情况(如通过 __new__ 控制)
# 更稳妥的做法:只统计非 VAR_POSITIONAL / VAR_KEYWORD 的普通参数
regular_params = [
p for p in params
if p.kind in (inspect.Parameter.POSITIONAL_ONLY,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
inspect.Parameter.KEYWORD_ONLY)
]
# 若存在 self,通常为第一个;移除它(但不强制索引,避免误判)
if regular_params and regular_params[0].name == 'self':
return len(regular_params) - 1
return len(regular_params)
except (ValueError, TypeError):
# 如内置类型(int, str)、C 扩展类可能不支持 signature
return 0
# 示例验证
class EmptyClass:
pass
class TwoArgClass:
def __init__(self, x, y):
self.x, self.y = x, y
class DefaultClass:
def __init__(self, a, b=1, c=2):
self.a = a
print(nb_args(EmptyClass)) # → 0(继承 object.__init__,仅 self)
print(nb_args(TwoArgClass)) # → 2
print(nb_args(DefaultClass)) # → 3(含带默认值的参数)
print(nb_args(int)) # → 0(内置类型,安全降级)
⚠️ 重要注意事项:
-
inspect.signature()可能对某些内置类型(如list,dict)或 C 实现类抛出ValueError,务必用try/except包裹; - 该方法统计的是签名中声明的参数,而非运行时实际调用所需的参数个数(例如
*args/**kwargs不计入); - 若类重写了
__new__且其签名更复杂,而__init__被省略,signature(cls)仍以__init__为准;如需严格匹配实例化行为,应结合__new__分析(但绝大多数场景__init__已足够); -
self参数始终被排除,这是符合直觉的设计——用户调用Cls(arg1, arg2)时无需传入self。
综上,nb_args() 函数提供了简洁、可靠且符合 Python 惯例的方式,帮助你在运行时动态探查类的构造契约,是构建灵活元编程逻辑的重要基础能力。










