那么从 class 语句到最终的类对象生成,解释器都做了些什么呢? 最直观的莫过于标准库模块 types 的源码了:
# Provide a PEP 3115 compliant mechanism for class creationdefnew_class(name,bases=(),kwds=None,exec_body=None):"""Create a class object dynamically using the appropriate metaclass.""" resolved_bases =resolve_bases(bases) meta, ns, kwds =prepare_class(name, resolved_bases, kwds)if exec_body isnotNone:exec_body(ns)if resolved_bases isnot bases: ns['__orig_bases__']= basesreturnmeta(name, resolved_bases, ns,**kwds)defresolve_bases(bases):"""Resolve MRO entries dynamically as specified by PEP 560.""" new_bases =list(bases) updated =False shift =0for i, base inenumerate(bases):ifisinstance(base,type):continueifnothasattr(base,"__mro_entries__"):continue new_base = base.__mro_entries__(bases) updated =Trueifnotisinstance(new_base,tuple):raiseTypeError("__mro_entries__ must return a tuple")else: new_bases[i+shift:i+shift+1]= new_base shift +=len(new_base)-1ifnot updated:return basesreturntuple(new_bases)defprepare_class(name,bases=(),kwds=None):"""Call the __prepare__ method of the appropriate metaclass. Returns (metaclass, namespace, kwds) as a 3-tuple *metaclass* is the appropriate metaclass *namespace* is the prepared class namespace *kwds* is an updated copy of the passed in kwds argument with any 'metaclass' entry removed. If no kwds argument is passed in, this will be an empty dict."""if kwds isNone: kwds ={}else: kwds =dict(kwds)# Don't alter the provided mappingif'metaclass'in kwds: meta = kwds.pop('metaclass')else:if bases: meta =type(bases[0])else: meta =typeifisinstance(meta,type):# when meta is a type, we first determine the most-derived metaclass# instead of invoking the initial candidate directly meta =_calculate_meta(meta, bases)ifhasattr(meta,'__prepare__'): ns = meta.__prepare__(name, bases,**kwds)else: ns ={}return meta, ns, kwdsdef_calculate_meta(meta,bases):"""Calculate the most derived metaclass.""" winner = metafor base in bases: base_meta =type(base)ifissubclass(winner, base_meta):continueifissubclass(base_meta, winner): winner = base_metacontinue# else:raiseTypeError("metaclass conflict: ""the metaclass of a derived class ""must be a (non-strict) subclass ""of the metaclasses of all its bases")return winner
在这段时期,一个名叫 mypy 的第三方包的兴起令 Python 核心开发者意识到了真正的问题所在:码农们对于静态类型的偏好其实源于对类型检查器的依赖。mypy 就是一个静态类型检查器,通过解析 Python 代码及其中包含的特定的注释,在 Python 代码实际运行前进行静态的类型检查。而这是一个兼容性非常棒的解决方案:类型声明的内容均包含在注释中,不会对原有的代码含义产生任何影响;类型检查独立工作在代码运行前,不会对代码的实际运行产生任何影响。2014 年 9 月 29 日,Python 之父 Guido van Rossum、mypy 之父 Jukka Lehtosalo 及 Python 核心开发者 Łukasz Langa 联手发布了 PEP 484 -- Type Hints ,在 Python 标准库中引入了全新的 typing 模块,提供对类型标注的官方支持。
Type concept is described above, types appear in variable and function type annotations, can be constructed from building blocks described below, and are used by static type checkers. 类型概念如上所述,类型出现在变量和函数类型注释中,可以从下面描述的构建块构造,并由静态类型检查器使用。
In Python, classes are object factories defined by the class statement, and returned by the type(obj) built-in function. Class is a dynamic, runtime concept. 在 Python 中,类是由 class 语句定义的对象工厂,并由 type(obj) 内置函数返回。 是一个动态的、运行时的概念。 —— PEP 483
Python 中由 Type Hints 及其标志性的 typing 模块代表的类型化是一种自顶向下的过程,从某种角度上来讲,这算是一种动态类型到静态类型的逆向工程
常见的强类型语言中 则是 自底向上
渐进式类型标注 Gradual typing allows one to annotate only part of a program, thus leverage desirable aspects of both dynamic and static typing. 渐进式类型化允许仅注解程序的一部分,从而充分利用动态和静态类型化的优点。 本文题目也正是自此而来。
every value from second_type is also in the set of values of first_type; second_type 中的每个值也在 first_type 的值集中; every function from first_type is also in the set of functions of second_type. first_type 中的每个函数也在 second_type 的函数集中。
推论
Every type is a subtype of itself.
每种类型都是其自身的子类型。
The set of values becomes smaller in the process of subtyping, while the set of functions becomes larger.
在子类型化的过程中,值的集合变得更小,而函数的集合变得更大。 pep483
When used in a type hint, the expression None is considered equivalent to type(None). 当在类型提示中使用时,表达式 None 被视为等同于 type(None) 。 pep484
客观问题 None 的存在违反了值和方法集合的变化规律,打破了原有的类型系统.
主观问题 很多时候其实它的作用只是一个 sentinel if 的简便写法忽略了类型问题 if Noneif [] 同为假