跳至主要内容

Type Checking in Python Tutorial

了解类型检查、各语言中的不同类型系统,以及鸭子类型与类型提示。
· 7分钟 阅读

使用 AI 探索

Python 通过其自身的解释器来进行类型检查。由于 Python 是一门动态语言,它不会强制用户为对象指定类型,这可能导致漏洞,错误也更难排查。为了解决这个问题,可以将 Python 与多种工具配合使用,在保留其鸭子类型特性的同时,引入静态类型检查。

不同的编程语言采用两种不同的类型系统,如下所示。

    1. 静态类型语言
    2. 动态类型语言

静态类型语言

变量类型的检查在编译期完成。此外,语言的类型系统会强制在使用变量之前显式声明其“数据类型”。静态类型的编程语言包括:Scala、Java、C++ 等。
例如,Scala 中字符串的声明如下:
var myCar:String = "Mercedes";
上述代码表明变量名“myCar”被显式声明为“String”类型,值为“Mercedes”。

动态类型语言

变量类型的检查在运行期完成。此外,语言的类型系统不会强制在使用变量之前显式声明其“数据类型”。动态类型的编程语言包括:Python、JavaScript、Ruby 等。
例如,Python 中字符串的定义如下:
myCar = "Mercedes"
上述代码表明变量名“myCar”无需显式声明类型。

Python 中的 'type()' 与 'isinstance()'

可以使用 Python 自带的 'type()' 来获知变量类型。'type()' 可在运行期用于调试,以识别程序中的精确变量类型。来看下面示例中一些变量的类型。

my_var = 12
print(type(my_var))

上述代码的输出为 'int'。因为该变量的数据类型是整数,而 'type()' 起到了识别的作用。

Python 的 'insinstance('obj','class')' 可用于判断对象 'obj' 是否是某个类的实例。返回值为布尔类型 True 或 False。

my_var = "Hello"
print(isinstance(my_var,str))

上述代码的输出为 True。因为变量的数据类型为字符串,'isinstance()' 起到了识别的作用。

Python 中的鸭子类型

在 Python 中有一个广为人知的原则:“如果它看起来像鸭子,游起来像鸭子,叫起来也像鸭子,那么它很可能就是鸭子。”该原则的含义很简单:对象或类的具体类型并不重要,只要对象包含相同的方法和属性,就可以将其用于特定目的。
下面通过一个示例更详细地说明上述说法。

看下面的例子,这里有两个类“Duck”和“Human”。Duck 类包含方法 quack 和 fly。类似地,Human 类也有自己的 'quack' 和 'fly' 方法。Duck 和 Human 是不同的类,也是不同的对象或类型。然而,Human 含有与 Duck 相同的 'fly' 和 'quack' 方法。还有一个方法 'fly_quack',它更像是针对“Duck”的,因为它可以飞也可以叫。

class Duck:
    def quack(self):
        print('Quack')
    def fly(self):
        print('Flap')
class Human:
    def quack(self):
        print('Trying to Quack like a duck')
    def fly(self):
        print('Spreading my arm to flap like a duck')      
def fly_quack(thing):
    thing.quack()
    thing.fly()

让我们创建一个 Duck 的实例 du,并传给函数 'fly_quack(du)'。同样地,创建一个 Human 的实例 'h' 并传给函数 'fly_quack(h)'。

du = Duck()
fly_quack(du)
h = Human()
fly_quack(h)

上述代码的输出如下:

Trying to Quack like a duck
Spreading my arm to flap like a duck
Quack
Flap

虽然“Human”与“Duck”是不同的类,但 Python 也会为 Human 调用函数 'fly_quack'。尽管对象类型不同,只要“Human”具有类似的 quack 和 fly 方法,它就会按预期行为并提供所需的方法。

类型提示与 mypy

像 Python 这样的动态类型语言有一个强大的方面:有助于提高代码的可访问性和可读性,但也存在一些缺点。其中一个缺点是在运行时发现错误,因为 Python 不强制变量类型,或者说它是动态类型语言。其结果可能产生错误与漏洞,且在代码规模越来越大时更难发现。

从 3.5 版本开始,Python 引入了类型提示(Type Hints)。更早的版本可能不支持类型提示功能。

下面先看一个没有类型提示和 mypy 的简单示例。

下面的函数用于整数的减法运算,它应当减去两个整数并返回值。

def sub_this(x,y):
    return 'Subtraction'
print(sub_this(8,'hello'))

这里的函数需要接收两个整数“x”和“y”,但由于未施加约束,可以接收任意数据类型。同时,返回值也可以是任何类型,代码返回了字符串 'Subtraction',而期望的是 'int'。

下面用类型提示和 'mypy' 来实现与上面相似的程序,这可以帮助进行静态类型检查,并更轻松地减少程序中的错误与漏洞。

'mypy' 是一个 Python 模块,用于进行静态类型检查,它结合了 Python 自身的动态检查(鸭子类型)与类型提示。

您需要安装 'mypy' 包
pip install mypy

您可以在本地创建一个名为 'mypy_example.py' 的文件,并输入以下代码。
下面的代码是一个简单程序,在参数中接收两个整数作为输入,'->' 后面标注返回的数据类型同样是 'int'。然而,该函数本应返回 int,却返回了字符串 'Subtracted two integers'。

def sub_this(x:int,y:int) -> int:
    return 'Subtracted two integers'
print(sub_this(8,4))

在终端中用以下命令运行上述代码。

mypy mypy_example.py

随后会显示错误,指示发现了意外的返回值 "str",需要改为 "int"。

Error message

将返回类型改为两个整数的相减,使其返回整数值。

def sub_this(x:int,y:int) -> int:
    return x - y
print(sub_this(8,4))

Success message

上述结果显示已打印成功信息,未发现问题。

恭喜您

恭喜您,已完成本教程的全部内容!

在本教程中,您了解了类型检查、不同语言的类型系统,以及 Python 方法('type()' 与 'isinstance()')、Python 的鸭子类型,以及结合 'mypy' 的类型提示。

如果您想进一步学习 Python,请查看 DataCamp 的 Software Engineering for Data Scientists in Python。

参考资料:

  1. Corey 的 YouTube 视频:Duck Typing

主题