本节书摘来自华章计算机《从问题到程序:用Python学编程和计算》一书中的第2章,第2.2节,作者:裘宗燕 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.2 数据对象、计算和类型
前面介绍了Python中可以表示和处理的几种数——整数、浮点数和复数,它们都是数学里的某类数的对应物,可以对它们做各种数学运算(用运算符描述),得到运算的结果。这些数都是程序操作的对象。
2.2.1 对象和类型
虽然整数、浮点数都是数,可以使用同样的运算符,但对数值相同的整数和浮点数做同样计算时,由于采用的规则不同,得到的结果也不同。例如:
>>> 12**30
237376313799769806328950291431424
>>> 12.0**30.0
2.373763137997698e+32
整数计算将得到任意大的准确结果,浮点数计算得到有限精度的近似结果 。
Python语言把在程序运行中能使用和处理的各种实体统称为对象(object),一个整数是一个对象,一个浮点数也是一个对象。在处理整数或浮点数字面量时,解释器创建相应的整数对象或浮点数对象,而计算也就是从一些对象算出另一些对象。在交互方式下,解释器把计算得到的结果对象显示出来给人看。
不同对象可能具有不同的性质。Python把性质相同、可以使用同一组操作、采用同样计算规则的一集对象称为一个类型。对属于同一类型的对象,解释器采用同样的处理方式。例如,整数就是一个类型,浮点数是另一个类型,复数又是另一个不同的类型。这几个类型有各自的字面量写法、可用运算的集合和运算规则。人们经常把整数类型简称为整型,把浮点数类型简称为浮点型。后面有时也这样说。
Python中的每个类型有一个名字(类型名),可以用特殊的方法得到:
>>> type(100)
<class 'int'>
>>> type(100.0)
<class 'float'>
>>> type(100+0j)
<class 'complex'>
第一个结果 表示字面量100描述的整数对象属于名字为int的类型,其他结果的意义类似。每个类型都有一个名字,称为类型名。整数类型的名字是int,浮点数的类型名是float,而复数的类型名是complex。对于任何描述对象的表达式,都可以用type取得其计算结果的类型:
>>> type(100 + 200)
<class 'int'>
>>> type(1.27 * 2.8)
<class 'float'>
int、float和complex统称为数值类型(numerical type)。这几个数值类型都是Python语言预先定义的类型,称为内置类型或者标准类型。
2.2.2 混合类型计算和类型转换
对数值类型的对象,Python允许做混合类型计算,也就是说,允许在一个表达式里出现不同数值类型的对象。例如:
>>> 2.7 * 3
8.100000000000001
>>> (2 + (1 + 3.4j)) * (5.78 - 3)
(8.34+9.452j)
自然,最后一个表达式得到的是复数类型的结果(对象)。
整数和浮点数各有自己的乘法运算规则,两者的运算规则不同。为了完成两个不同类型的对象之间的运算,Python严格规定了采用的规则。在这里的规定是:如果遇到表达式要求做一个整数和一个浮点数的运算,那么就先从那个整数转换得到一个“与之等值”的浮点数对象,而后再按浮点数的规则计算,得到浮点数结果。如果是整数(或浮点数)与复数运算,就先从该数转换得到一个复数,然后再计算。注意,这里说转换,术语是类型转换,实际上原来的数并不改变,而是根据该数按规则做出另一个满足需要的数。
上面的“与之等值”加了引号,是想说明这个说法并不准确。整数可以任意大,具有任意位精度。用浮点数表示,或许能表示其近似值(因此并不与之等值),甚至无法表示它(由于整数太大而溢出)。这是混合类型计算中可能出现的情况,必须注意。
总结一下:对混合类型的计算表达式,Python解释器将自动安排适当的转换,使表达式描述的计算得以进行。这种转换是解释器自动完成的操作,从已有的某类型对象出发,做出一个与之相关的具有所需类型的对象。在上面第二个例子里,先根据2做出一个复数,再用它与复数(1+3.4j)相加;再从整数3做出一个浮点数并从5.78里减去它;再从第二个计算的结果(一个浮点数)做出一个复数并完成两个复数的乘法。可见,要理解复杂表达式的意义,不仅要关注其中的计算如何进行,还要注意哪些地方出现了类型转换,各为从什么类型转到什么类型,具体计算是在哪个类型里进行的。
有时,自动完成的转换不符合实际计算的需要。出现这种情况时,我们就需要在表达式里说明期望的转换,相应描述称为强制类型转换。强制类型转换通过类型名描述,描述形式与前面写type的形式类似。例如:
>>> int(2.37**5.6) * 4
500
这里int(2.37**5.6)要求把浮点数计算的结果转换为整数,然后用它乘以4得到整数结果。用int将浮点数转换到整数的规则是丢掉小数,取得整数部分。
如果写表达式float(12**20),可以得到括号里整数计算结果的浮点数近似值。但Python不允许用float或int去转换复数对象,数学里也没有相应的规则。如果对不能转换的对象做转换,解释器也会报告错误。例如:
>>> float((2 + 1.2j)**3)
Traceback (most recent call last):
File "<pyshell#69>", line 1, in <module>
float((2 + 1.2j)**3)
TypeError: can't convert complex to float
取得复数的实部、虚部或者模的问题在后面说明。
类型名complex可用于构造复数:
>>> complex(12**20, 1.25**20)
(3.833759992447475e+21+86.73617379884035j)
括号里逗号分隔的两个表达式分别表示要构造的复数的实部和虚部。
最后考虑一个实际应用题:已知三角形的三边长度分别为5、7和11厘米,现在希望求出这个三角形的面积。
显然,为完成这个任务,首先需要找到从三边长求面积的方法。数学知识已经给出了相应的面积公式:
![a8403ed707b8360a78b150072e266d84ee2b22f3](https://i-blog.csdnimg.cn/blog_migrate/74189cf7d6c2e16a3f536cc6bd234813.png)
其中s是三角形的半周长, 。根据这些公式,不难写出下面表达式:
>>> ((5 + 7 + 11)/2 * ((5 + 7 + 11)/2 - 5) *
((5 + 7 + 11)/2 - 7) * ((5 + 7 + 11)/2 - 11)) ** 0.5
12.968712349342937
为了写出这个长表达式,我们特别在前面多写了一个括号。在表达式换行时,因为有括号没配对,解释器自动把后一行当作续行。显然,这个表达式虽然解决了问题,但其形式不太令人满意,其中出现繁琐的重复。2.5节将解决这个问题。
2.2.3 数值类型和计算的简单总结
现在对本章中至此有关数值类型计算的讨论做一点总结。
Python语言提供了三个基本数值类型:int、float和comlex,规定了各种数字面量的描述形式,以便人们直接描述计算中使用的数值对象。Python的整数对象可以表示任意大的整数值,但浮点数(和复数)只能表示有限范围内精度有限的数值。
表示数值计算的表达式基于数值字面量、算术运算符和括号描述,形式上是一维的单词序列,序列中的单词和序列的构成形式都必须满足表达式的语法要求。满足规定(满足语法)的表达式才有意义(语义)。Python严格规定了单词和表达式的形式,所采用的形式解释器容易处理,人可以接受和习惯。遇到不合法的单词,或者构成形式不合法的表达式,解释器将报错,并能标明发现错误的位置。
合法的表达式描述了一个计算过程。如果解释器处理一个表达式的计算能顺利完成,就会求出该表达式的值。但是,满足语法的表达式在计算中也可能出错,如3/0、3.0/0.0在计算中就会报告除以0的动态运行错误。此外,浮点数运算的结果可能超出浮点数的表示范围,这种情况称为溢出。出现溢出时得到一个特殊结果。
表达式描述的求值过程由多方面的因素确定:运算符有优先级和结合方式,高优先级的运算符先执行,相同优先级的运算符按结合顺序执行。如果由优先级和结合方式默认确定的计算顺序不合适,可以加入括号明确指定所需计算顺序。
算术运算符分为一元运算符(正负号)和二元运算符,对二元运算符,在做它所要求的计算之前,总是先计算其左边的运算对象,再计算右边的运算对象。
参与计算的对象都有类型,计算的结果也有类型,对象的类型决定计算的方式和结果。两个同样类型的数值对象直接参与运算,运算结果一般也具有同样类型(整数除法运算符 / 的情况特殊,其结果是浮点数)。用type可以得到表达式的计算结果的类型(注意,字面量是最简单的表达式)。
如果参与一个二元运算的两个对象的类型不同,解释器先把它们转换为同样类型后再计算。整数与浮点数或复数运算,解释器自动由它做出一个浮点数或复数;浮点数与复数运算,解释器自动由这个浮点数做出一个复数。如果混合类型计算中的自动转换不合乎需要,可以人工描述转换。用类型名int可以构造与给定浮点数对应的整数值(取整)。
应特别注意,做数值转换时可能出错。在把一个整数转换为一个浮点数时,得到的结果可能超出浮点数的表示范围。还需注意,整数计算是精确计算,总得到精确的整数结果(除了使用运算符 / 的情况),浮点数计算和复数计算是近似计算,总得到近似结果,计算中可能出现误差,需要特别当心。这个问题后面还会讨论。