关于NewType创建子类型的核心意图及与类继承差异的技术问询
NewType的核心意图与类继承子类的区别
一、NewType的主要意图
typing.NewType 是Python类型检查工具(如mypy)用来创建名义子类型的工具,核心目的是:
- 在类型检查层面区分底层类型相同但语义不同的值,避免无意识的混用。比如
Age = NewType("Age", int)和UserId = NewType("UserId", int),虽然底层都是int,但类型检查会阻止把Age赋值给UserId,确保语义正确性。 - 不需要创建实际的类对象,完全是类型检查阶段的概念,运行时没有额外开销——
NewType创建的"类型"在运行时只是一个标识函数,调用后直接返回原始值(比如Age(25)运行时就是25)。
你的代码报错的直接原因:链式定义NewType时,子类型的构造函数要求传入父类型的实例,而非原始底层类型。比如SubType1 = NewType("SubType1", BaseType),所以SubType1的参数必须是BaseType实例,不能直接传int。修正后的代码应该是:
from typing import NewType BaseType = NewType("BaseType", int) SubType1 = NewType("SubType1", BaseType) SubType2 = NewType("SubType2", BaseType) def return_age(age: BaseType) -> BaseType: return age return_age(SubType1(BaseType(666))) # 此时mypy不会报错 return_age(SubType2(BaseType(666)))
二、NewType子类型与类继承子类的核心区别
1. 运行时本质
- NewType子类型:完全是类型检查的"虚拟类型",运行时不存在实际的类型对象。
SubType1(BaseType(666))在运行时就是整数666,和int实例没有区别。 - 类继承子类:是真正的对象实例,有独立的身份标识,运行时和父类实例是不同的对象(比如
SubClass1()的类型是SubClass1,而非BaseClass)。
2. 子类型兼容性规则
- 类继承:遵循里氏替换原则,子类是父类的隐式子类型,子类实例可以直接传给接受父类的函数,无需显式转换。比如你的类继承示例中,
SubClass1()可以直接传入return_class,因为SubClass1是BaseClass的子类型,类型检查器自动认可这种兼容。 - NewType:是严格的名义子类型,仅在显式构造后才能建立类型关系:
- 子类型实例可以传给接受父类型的函数(比如
SubType1实例可以传给return_age); - 父类型实例不能直接传给接受子类型的函数(必须显式用子类型构造函数包裹);
- 底层原始类型(比如int)不能直接传给子类型的构造函数,必须先转成父类型(这就是你代码报错的原因)。
- 子类型实例可以传给接受父类型的函数(比如
3. 用途场景
- NewType:适合处理"语义不同但底层类型相同"的简单值,比如各种ID、计数、编码等,不需要额外的属性或方法,只需要类型检查层面的区分。
- 类继承:适合需要扩展行为(添加方法、属性)、维护状态,或者需要运行时多态的场景,子类可以拥有父类没有的特性,同时保持向上兼容。
内容的提问来源于stack exchange,提问作者vildhjarta
相关产品推荐
相关产品推荐

