python运行时通过对象实例的__class__属性动态确定类型,而非变量声明;type()仅检查确切类,isinstance()支持继承体系和协议检查,所有“类型错误”实为属性缺失或协议不满足引发的具体异常。

Python 运行时怎么知道变量是什么类型
Python 不在编译期绑定类型,而是在每次访问对象属性、调用方法或执行操作时,才去查这个对象当前的 __class__ 和它的类型行为。换句话说:不是“解析类型”,而是“查对象身上的东西”。
常见错误现象:AttributeError: 'int' object has no attribute 'append' —— 这不是类型检查失败,是运行到 .append() 这一刻,发现 int 对象根本没有这个属性,直接抛异常。
- 所有类型信息都附着在对象实例上,而不是变量名上;
x = 42后,x只是个指向int实例的引用,不存“类型声明” -
type(x)和isinstance(x, int)都是运行时查x.__class__,不是推导或解析 - 函数参数、返回值标注(如
def f(x: str) -> int:)完全不参与运行时行为,仅用于工具(mypy)或文档
为什么 hasattr() / getattr() 有时“看起来像类型判断”
因为开发者常靠探测属性是否存在来绕过类型限制,但这本质是鸭子类型实践,不是类型系统介入。
使用场景:写通用处理函数,要兼容 list、tuple、str 等有 __len__ 的对象,但不想硬写 isinstance(..., (list, tuple, str))。
立即学习“Python免费学习笔记(深入)”;
-
hasattr(obj, '__len__')查的是对象当前有没有这个属性(可能动态加了),不是查它“该不该有” -
getattr(obj, 'upper', None)安全取方法,但若obj是自定义类且覆盖了__getattribute__,结果可能出人意料 - 性能影响:每次调用都触发属性查找链(
__getattribute__→__dict__→__slots__→ 父类),比直接调用慢一截
type() 和 isinstance() 的关键区别在哪
type() 只认“是不是这个确切类”,isinstance() 认“是不是这个类或其任意子类”——这是继承体系能否被正确识别的核心分水岭。
常见错误现象:type(True) is int 返回 True(因为 bool 是 int 子类),但 type(True) == int 也成立,而 isinstance(True, int) 同样为 True;可一旦你写 type(x) is list,就漏掉了 collections.UserList 这类子类实例。
- 绝大多数情况该用
isinstance(x, list),除非你明确要排除所有子类 -
isinstance(x, (list, tuple))支持元组参数,type(x)不支持 - 自定义类如果重写了
__instancecheck__,isinstance()行为可被修改;type()始终不可干预
运行时“类型错误”其实都是属性/方法缺失或协议不满足
Python 没有“类型错误”这个运行时异常类别,所有报错都落在 AttributeError、TypeError(如参数个数不对、不可哈希)、ValueError(如 int('abc'))这些具体语义异常上。
典型例子:1 + '2' 报 TypeError: unsupported operand type(s) for +: 'int' and 'str',这不是类型系统阻止了运算,而是 int.__add__ 方法看到右操作数不是数字类型,主动返回 NotImplemented,然后解释器尝试调用 str.__radd__,也返回 NotImplemented,最终抛异常。
- 协议(protocol)比类型更重要:只要实现了
__iter__,就能用在for里;只要实现了__call__,就能加括号调用 - 第三方库如
typing.Protocol是静态检查用的,运行时完全无感知 - 真正容易被忽略的是:某些 C 扩展类型(如
numpy.ndarray)会绕过部分 Python 层协议,导致isinstance(x, collections.abc.Sequence)返回False却仍能迭代










