购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1 类型提示

在学习如何创建类之前,我们先讨论一下什么是类,以及如何确定我们正在正确地使用它。这里的中心思想是:Python中的一切都是对象。

当我们写出像“Hello,world!”或42这样的字面量时,我们实际上是在创建内置类的实例。我们可以打开交互式Python,使用内置的type()函数查看这些对象所属的类:

面向对象编程的重点是通过对象的交互来解决问题。当我们写6*7时,两个整数相乘是由int类型的一个方法处理的。对于更复杂的行为,我们通常需要编写特定的新类。

下面是Python对象的两个核心规则:

· Python中的一切都是对象。

· 任何一个对象都至少是一个类的实例。

这些规则会产生有趣的效果。当我们使用class语句定义一个类的时候,我们创建了一个type类型的对象。当我们创建一个类的 实例 时,它的class对象会用于创建和初始化这个实例对象。

类(class)和类型(type)有什么区别?使用class语句可以定义新的类型(type)。实际上它们可以混着用。在本书中,我们通常使用类,必要的时候也会使用类型(type)。在Eli Bendersky所著的 Python object,types,classes,and instances-a glossary (链接6)中有一句很有用的话:

“术语‘类’和‘类型’是同一个概念的两个名称。”

我们将遵循常规,把注解称为类型提示。

还有另一个重要的规则:

· 变量是对对象的引用,可以想象成将写着名字的便笺纸贴在一个东西上。变量是那个便笺纸,东西是对象。

这不是什么惊天动地的规则,但实际上挺酷的。它意味着对象的类型与对象所关联的类有关,与指向对象的变量没有任何关系。下面的代码是有效的,但很让人困惑:

我们先用内建的str类创建了一个对象,并给这个对象赋予了一个很长的名称a_string_variable。然后,我们用另一个内建的int类创建了一个对象,并赋予了它同一个名称。(原来的字符串对象没被引用了,它会被销毁。)

下面并排的两个步骤显示了变量如何从一个对象转移到另一个对象,如图2.1所示。

图2.1 变量名和对象

各种属性是对象的一部分,但不是变量的一部分。当我们使用type()函数检查一个变量的类型时,我们看到的是变量指向的对象的类型。变量本身并没有类型,它只是一个名称而已。类似地,使用id()函数检查一个变量,显示的是这个变量指向的对象的内存地址(ID)。如果我们给整数对象取名a_string_variable,那会有点儿误导人。

类型检查

我们进一步深入学习对象和类型的关系,看看这些规则的更多结果。下面是一个函数的定义:

这个函数接收一个参数n,n对2取余。如果n是偶数,则余数为0,返回False;如果n是奇数,则余数为1,返回True。简单地说,这个函数会判断数字 n 是否为奇数。

如果参数不是数字,会发生什么?我们试一下(试一下是学习Python的好方法)。在交互式解释器中把“Hello,World”传给odd()函数,会得到类似这样的结果:

这是Python超灵活规则的一个重要结果:没有什么能阻止我们做一些可能引发异常的蠢事。这是一个重要的提示:

Python不会阻止我们尝试使用并不存在的对象方法。

在我们的示例中,%运算符在str类中的行为和在int类中的是不同的,因此抛出了异常。%在字符串中用得不多,但它是有用的,用于在字符串中插入变量值:代码"a=%d"%113会产生一个字符串'a=113'。如果字符串中没有类似%d这样的格式占位符,给字符串做%运算就会抛出TypeError异常。 对整数来说,%用来计算除法的余数:355%113返回整数16。

这种灵活性反映了Python设计者做出的一种权衡:易用性优先于复杂的预防错误机制。这让人们在使用变量名时更加简单和轻松。

Python的内部运算符会检查操作数是否满足运算符的要求。然而,我们编写的函数定义不包括任何运行时类型检查,我们也不想为运行时类型检查添加代码。相反,作为测试的一部分,我们使用工具来检查代码。我们可以提供被称为 类型提示 的注解,并使用工具检查我们的代码,以确保参数符合类型提示要求。

我们先来看一下这种注解。在几种情况下,我们可以在变量名后加上一个冒号和变量类型(比如age:int)来指定变量的类型。我们可以在函数(和方法)的参数中这么做,也可以在赋值语句中这么做。另外,我们还可以用->语法说明函数(或一个类方法)的返回值。

这是一个类型提示的示例:

我们给odd()函数添加了两个类型提示。我们指定了参数n的值应该是int类型的,也指定了函数的结果是Boolean类型的。

虽然类型提示会占用一点儿存储空间,但它们对运行时没有影响。Python在运行时会忽略这些提示,因为它们是可选的。但是,阅读你的代码的人会很高兴看到它们,它们是告知读者你的意图的好方法。你可以选择先不学习它们,但是当你回去扩展你之前写的东西时,你会喜欢它们。

mypy 是一个常用的类型检查工具。它不是Python自带的,需要单独下载和安装。我们会在本章后面的第三方库中讨论虚拟环境和工具的安装。现在你可以使用这个命令安装:python-m pip install mypy。如果你用 conda ,则请使用conda install mypy命令。

假设src目录下有一个文件bad_hints.py,里面有两个函数及几行调用main()函数的代码:

当我们在操作系统的终端提示下运行mypy命令时:

mypy 工具会检查出一些潜在的问题,至少包括这些:

上面提示错误发生在第12和第13行,因为在我们的文件中还有很多注释没显示在上面。在你的版本中,错误可能发生在第1行。有两个错误:

· main()函数没有返回值类型;如果函数确实没有返回值, mypy 建议使用->None显式声明这一点。

· 更重要的是第13行,代码试图在调用odd()函数的时候传入一个str值。这与odd()函数要求的参数类型不符。

本书的大部分示例中会使用类型提示。虽然它们是可选的,但我们认为它们总是有用的,尤其是在编程教学过程中。因为Python的大多数类型都是通用的,所以有些Python函数确实支持很多不同的类型,因此无法用作类型提示,我们将在本书中避开这些极端情况。

Python Enhancement Proposal(PEP) 585涵盖了一些新的语言特性,使类型提示更简单一些。我们使用 mypy 0.812版本来测试本书中的所有示例。若使用老的版本,则会遇到一些新语法和注解问题。

我们已经讨论了如何使用类型提示来描述函数参数和对象属性,现在让我们实际构建一些类。 nx2hQQebMlYX09ymkedu3tM+Q/5+2Ne/n+T6GX0eEbUUw93gpuhkqXX1oq8hy4WA

点击中间区域
呼出菜单
上一章
目录
下一章
×