怎么理解 python3 中的 NewType
背景
python3 为了增强类型提示,在标准库中新加了一个 typing 库,其中 NewType
部分的例子最简单,但是也最能理解官方要表达什么。以至于成了我多年以来的一块心病,今天早上晨读突然就顿悟了。啪的一下很快啊!
例子
这里以一个从 html 文档中抽取 title 值的函数做例子,先来看一下没有类型提示、文档注释的版本。
from bs4 import BeautifulSoup
def get_title(document):
soup = BeautifulSoup(document,"html5lib")
return soup.title.text
这样的话使用这个函数的人就要看源代码才能明白函数的功能。
加文档注释
为了代码的使用方好过一点,决定还是加几行文档注释(numpy 风格)。
def get_title(document):
"""
返回 html 文档的 title 值
document:
--------
html 文档字符串
return:
-------
str
"""
soup = BeautifulSoup(document,"html5lib")
return soup.title.text
虽然注释是有了,但是新的问题是我们的注释比代码都长,感觉自己不是来写代码的是来写注释的。
普通类型提示
为了不要让我们的主要精力花在写注释上,决定换一种技术方案,那就是写类型提示。
def get_title(document:str) -> str:
soup = BeautifulSoup(document,"html5lib")
return soup.title.text
这下是简了,除了告诉别人函数接收一个 str 返回一个 str 之外,其它的信息就太少了,不方便使用方理解函数的功能。
使用 NewType
上面的代码有一个毛病就是 str
的语义是不明确的,也就是说看不出为 document 是一个怎样的 str ?它有没有格式?如果有,格式是怎样的?为了把语义搞的更加明确我们可以用 NewType
。
from typing import NewType
from bs4 import BeautifulSoup
Html = NewType("Html",str)
Title = NewType("Title",str)
def get_title(document:Html) -> Title:
"""
返回 html 文档的 title 值
"""
soup = BeautifulSoup(document,"html5lib")
return soup.title.text
NewType 背后的原理
从上面代码中我们可以看到 Html
这个对象是因为我们需要一个比 str
更加具体的描述才创造出来的。也就是说它没有什么功能性的需求在里面, def f(x): return x
就是没有任何功能的典型代表。
1、NewType 本身是函数
In [1]: from typing import NewType
In [2]: type(NewType)
Out[2]: function
2、由 NewType 创建出来的对象是一个把输入参数直接返回的函数。
In [1]: from typing import NewType
In [2]: Html = NewType("Html",str)
In [3]: s = "hello world"
In [4]: s == Html(s)
Out[4]: True
In [5]: def f(x): return x # Html 的源码和这个函数是一样的
In [6]: s == f(s)
Out[6]: True
In [7]: type(f)
Out[7]: function
In [8]: type(Html)
Out[8]: function
#可以看到 Html 和 函数 f 的字节码是一样的。
In [9]: from dis import dis
In [10]: dis(f)
1 0 LOAD_FAST 0 (x)
2 RETURN_VALUE
In [11]: dis(Html)
1823 0 LOAD_FAST 0 (x)