一小时学会Python数字计算(文末福利)

本文摘自人民邮电出版社《Python程序设计(第3版)》书

学习目标

  • 理解数据类型的概念。

  • 熟悉Python中的基本数值数据类型。

  • 理解数字在计算机上如何表示的基本原理。

  • 能够使用Python的math库。

  • 理解累积器程序模式。

  • 能够阅读和编写处理数值数据的程序。

1.1 数值数据类型

计算机刚开发出来时,它们主要被视为数字处理器,现在这仍然是一个重要的应用。如你所见,涉及数学公式的问题很容易转化为Python程序。在本文中,我们将仔细观察一些程序,它们的目的是执行数值计算。

计算机程序存储和操作的信息通常称为“数据”。不同种类的数据以不同的方式存储和操作。请考虑这个计算零钱的程序:

# change.py
#  A program to calculate the value of some change in dollars
def main():
  print("Change Counter")
  print()
  print("Please enter the count of each coin type.")
  quarters = eval(input("Quarters: "))
  dimes = eval(input("Dimes: "))
  nickels = eval(input("Nickels: "))
  pennies = eval(input("Pennies: "))
  total = quarters * .25 + dimes * .10 + nickels * .05 + pennies * .01
  print()
  print("The total value of your change is", total)
main()

下面是输出示例:

Change Counter Please enter the count of each coin type.
Quarters: 5
Dimes: 3
Nickels: 4
Pennies: 6
The total value of your change is 1.81

这个程序实际上操作两种不同的数字。用户输入的值(5,3,4,6)是整数,它们没有任何小数部分。硬币的值(.25,.10,.05,.01)是分数的十进制表示。在计算机内部,整数和具有小数部分的数字以不同的方式存储。从技术上讲,这是两种不同的“数据类型”。

对象的数据类型决定了它可以具有的值以及可以对它执行的操作。整数用“integer”数据类型(简写为“int”)表示。int类型的值可以是正数或负数。可以具有小数部分的数字表示为“floating-point(浮点)”(或“float”)值。那么我们如何判断一个数值是int还是float呢?不包含小数点的数值字面量生成一个int值,但是具有小数点的字面量由float表示(即使小数部分为0)。

Python提供了一个特殊函数,名为type,它告诉我们任何值的数据类型(或“class”)。下面是与Python解释器的交互,显示int和float字面量之间的区别:

>>> type(3)
<class 'int'>
>>> type(3.14)
<class 'float'>
>>> type(3.0)
<class 'float'>
>>> myInt = -32
>>> type(myInt)
<class 'int'>
>>> myFloat = 32.0
>>> type(myFloat)
<class 'float'>

你可能希望知道,为什么有两种不同的数据类型。一个原因涉及程序风格。表示计数的值不能为小数,例如,我们不能有3.12个季度。使用int值告诉读者程序的值不能是一个分数。另一个原因涉及各种操作的效率。对于int,执行计算机运算的基础算法更简单,因此可以更快,而float值所需的算法更通用。当然,在现代处理器上,浮点运算的硬件实现是高度优化的,可能与int运算一样快。

int和float之间的另一个区别是,float类型只能表示对实数的近似。我们会看到,存储值的精度(或准确度)存在限制。由于浮点值不精确,而int总是精确的,所以一般的经验法则应该是:如果不需要小数值,就用int。

值的数据类型决定了可以使用的操作。如你所见,Python支持对数值的一般数学运算。表1.1总结了这些操作。实际上,这个表有些误导。由于这两种类型具有不同的底层表示,所以它们各自具有不同的一组操作。例如,我只列出了一个加法操作,但请记住,对float值执行加法时,计算机硬件执行浮点加法,而对int值,计算机执行整数加法。Python基于操作数选择合适的底层操作(int或float)。

表1.1  Python内置的数值操作

操作符操作

+

-

*

/

浮点除

**

指数

abs()

绝对值

//

整数除

%

取余

请考虑以下Python交互:

>>> 3 + 4
7
>>> 3.0 + 4.0
7.0
>>> 3 * 4
12
>>> 3.0 * 4.0
12.0
>>> 4 ** 3
64
>>> 4.0 ** 3
64.0
>>> 4.0 ** 3.0
64.0
>>> abs(5)
5
>>> abs(-3.5)
3.5
>>>

在大多数情况下,对float的操作产生float,对int的操作产生int。大多数时候,我们甚至不必担心正在执行什么类型的操作。例如,整数加法与浮点加法产生的结果几乎相同,我们可以相信Python会做正确的事情。

然而,在除法时,事情就比较有趣了。如表所列,Python(版本3.0)提供了两种不同的运算符。通常的符号(/)用于“常规”除法,双斜线(//)用于表示整数除法。找到它们之间差异的最佳方法就是试一下。

>>> 10 / 3
3.3333333333333335
>>> 10.0 / 3.0
3.3333333333333335
>>> 10 / 5
2.0
>>> 10 // 3
3
>>> 10.0 // 3.0
3.0
>>> 10 % 3
1
>>> 10.0 % 3.0
1.0

请注意,“/”操作符总是返回一个浮点数。常规除法通常产生分数结果,即使操作数可能是int。Python通过返回一个浮点数来满足这个要求。10/3的结果最后有一个5,你是否感到惊讶?请记住,浮点值总是近似值。该值与Python将表示为浮点数时得到的近似值相同。

要获得返回整数结果的除法,可以使用整数除法运算“//”。整数除法总是产生一个整数。 把整数除法看作gozinta(进入或整除)。表达式10 // 3得到3,因为3 进入 10共计3次(余数为1)。虽然整数除法的结果总是一个整数,但结果的数据类型取决于操作数的数据类型。浮点整数整除浮点数得到一个浮点数,它的分数分量为0。最后两个交互展示了余数运算%。请再次注意,结果的数据类型取决于操作数的类型。

由于数学背景不同,你可能没用过整数除法或余数运算。要记住的是,这两个操作是密切相关的。整数除法告诉你一个数字进入另一个数字的次数,剩余部分告诉你剩下多少。数学上你可以写为a = (a//b)(b) + (a%b)。

作为示例应用程序,假设我们以美分来计算零钱(而不是美元)。如果我有383美分,那么我可以通过计算383 // 100 = 3找到完整美元的数量,剩余的零钱是383%100 = 83。因此,我肯定共有3美元和83美分的零钱。

顺便说一句,虽然Python(版本3.0)将常规除法和整数除法作为两个独立的运算符,但是许多其他计算机语言(和早期的Python版本)只是使用“/”来表示这两种情况。当操作数是整数时,“/”表示整数除法,当它们是浮点数时,它表示常规除法。这是一个常见的错误来源。例如,在我们的温度转换程序中,公式9/5 * celsius + 32不会计算正确的结果,因为9/5将使用整数除法计算为1。在这些语言中,你需要小心地将此表达式编写为9.0 / 5.0 * celsius + 32,以便使用正确的除法形式,从而得到分数结果。

1.2 类型转换和舍入

在某些情况下,值可能需要从一种数据类型转换为另一种数据类型。你已知道,int和int组合(通常)产生一个int,float和float组合创建另一个float。但是如果我们写一个混合int和float的表达式会发生什么呢?例如,在下列赋值语句之后,x的值应该是什么:

x = 5.0 * 2

如果这是浮点乘法,则结果应为浮点值10.0。如果执行整型乘法,结果就是10。在继续读下去获得答案之前,请花一点时间考虑:你认为Python应该怎样处理这种情况。

为了理解表达式5.0 * 2,Python必须将5.0转换为5并执行int操作,或将2转换为2.0并执行浮点操作。一般来说,将float转换为int是一个危险的步骤,因为一些信息(小数部分)会丢失。另一方面,int可以安全地转换为浮点,只需添加一个小数部分0。因此,在“混合类型表达式”中,Python会自动将int转换为浮点数,并执行浮点运算以产生浮点数结果。

有时我们可能希望自己执行类型转换。这称为显式类型转换。Python为这些场合提供了内置函数int和float。以下一些交互示例说明了它们的行为:

>>> int(4.5)
4
>>> int(3.9)
3
>>> float(4)
4.0
>>> float(4.5)
4.5
>>> float(int(3.3))
3.0
>>> int(float(3.3))
3
>>> int(float(3))
3

如你所见,转换为int就是丢弃浮点值的小数部分,该值将被截断,而不是舍入。如果你希望一个四舍五入的结果,假设值为正,可以在使用int()之前加上0.5。对数字进行四舍五入的更一般方法是使用内置的round函数,它将数字四舍五入到最接近的整数值。

>>> round(3.14)
3
>>> round(3.5)
4

请注意,像这样调用round会产生一个int值。因此,对round的简单调用是将float转换为int的另一种方法。

如果要将浮点值舍入为另一个浮点值,则可以通过提供第二个参数来指定在小数点后的数字位数。下面的交互处理π的值:

>>> pi = 3.141592653589793
>>> round(pi, 2)
3.14
>>> round(pi,3)
3.142

请注意,当我们将π近似舍入到两位或三位小数时,我们得到一个浮点数,其显示值看起来像一个完全舍入的结果。记住,浮点值是近似。真正得到的是一个非常接近我们要求的值。实际存储的值类似于3.140000000000000124345……,最接近的可表示的浮点值为3.14。幸运的是,Python是聪明的,知道我们可能不希望看到所有这些数字,所以它显示了舍入的形式。这意味着如果你编写一个程序,将一个值四舍五入到两位小数,并打印出来,就会看到两位小数,与你期望的一样。在第5章中,我们将看到如何更好地控制打印数字的显示方式,那时如果你希望,就能查看所有的数字。

类型转换函数int和float也可以用于将数字字符串转换为数字。

>>> int("32")
32
>>> float("32")
32.0
>>> float("9.8")
9.8

作为替代eval从用户获取数字数据的另一种方法,这特别有用。例如,下面是本章开始时零钱计数程序的一个改进版本:

# change2.py
#  A program to calculate the value of some change in dollars
def main():
  print("Change Counter")
  print()
  print("Please enter the count of each coin type.")
  quarters = int(input("Quarters: "))
  dimes = int(input("Dimes: "))
  nickels = int(input("Nickels: "))
  pennies = int(input("Pennies: "))
  total = .25*quarters + .10*dimes + .05*nickels + .01*pennies
  print()
  print("The total value of your change is", total)
main()

在input语句中使用int而不是eval,可以确保用户只能输入有效的整数。任何非法(非int)输入将导致程序崩溃和错误消息,从而避免代码注入攻击的风险(在第2.5.2节讨论)。另一个好处是,这个版本的程序强调输入应该是整数。

使用数字类型转换代替eval的唯一缺点是,它不支持同时输入(在单个输入中获取多个值),如下例所示:

>>> # simultaneous input using eval
>>> x,y = eval(input("Enter (x,y): "))
Enter (x,y): 3,4
>>> x
3
>>> y
4
>>> # does not work with float
>>> x,y = float(input("Enter (x,y): "))
Enter (x,y): 3,4
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: '3,4'

这个代价很小,换来了额外的安全性。在第5章,你将学习如何克服这个限制。作为一种良好的实践,你应该尽可能使用适当的类型转换函数代替eval。

1.3 使用math库

除表1.1中列出的操作之外,Python还在一个特殊的math“库”中提供了许多其他有用的数学函数。库就是一个模块,包含了一些有用定义。我们的下一个程序展示了使用这个库来计算二次方程的根。

二次方程的形式为ax2 + bx + c = 0。这样的方程有两个解,由求根公式给出:

让我们编写一个程序,找到二次方程的解。程序的输入将是系数a、b和c的值,输出是由求根公式给出的两个值。下面是完成这项工作的程序:

# quadratic.py
#  A program that computes the real roots of a quadratic equation.
#  Illustrates use of the math library.
#  Note: This program crashes if the equation has no real roots.
import math # Makes the math library available.
def main():
  print("This program finds the real solutions to a quadratic")
  print()
  a = float(input("Enter coefficient a: "))
  b = float(input("Enter coefficient b: "))
  c = float(input("Enter coefficient c: "))
  discRoot = math.sqrt(b * b - 4 * a * c)
  root1 = (-b + discRoot) / (2 * a)
  root2 = (-b - discRoot) / (2 * a)
  print()
  print("The solutions are:", root1, root2 )
main()

该程序使用了math库模块的平方根函数sqrt。在程序的顶部,import math告诉Python我们正在使用math模块。导入模块让程序中定义的任何内容都可用。要计算,我们使用math.sqrt(x)。这个特殊的点符号告诉Python,使用“生存”在math模块中的sqrt函数。在二次方程程序中,我们用下面的代码行来计算

discRoot = math.sqrt(b * b - 4 * a * c)

下面是程序运行的情况:

This program finds the real solutions to a quadratic
Enter coefficient a: 3
Enter coefficient b: 4
Enter coefficient c: -2
The solutions are: 0.38742588672279316 -1.7207592200561266

只要我们要解的二次方程有实数解,这个程序就很好。但是,一些输入会导致程序崩溃。下面是另一个运行示例:

This program finds the real solutions to a quadratic
Enter coefficient a: 1
Enter coefficient b: 2
Enter coefficient c: 3
Traceback (most recent call last):
 File "quadratic.py", line 21, in ?
  main()
 File "quadratic.py", line 14, in main
  discRoot = math.sqrt(b * b - 4 * a * c)
ValueError: math domain error

这里的问题是b2 − 4ac < 0,sqrt函数无法计算负数的平方根。Python打印“math domain error”。这告诉我们,负数不在sqrt函数的定义域中。现在,我们没有工具来解决这个问题,所以我们只需要假设用户会给我们可解的方程。

实际上,quadratic.py不需要使用math库。我们可以用乘方**来取平方根。(你知道怎么做吗?)使用math.sqrt更高效一些,而且它让我展示使用math库。一般来说,如果你的程序需要一个通用的数学函数,首先要看看math库。表3.2显示了math库中提供的一些其他函数。

表1.2  一些math库函数

Python数学描述

pi

π

π的近似值

e

e

e的近似值

sqrt(x)

 

x的平方根

sin(x)

sin x

x的正弦

cos(x)

cos x

x的余弦

tan(x)

tan x

x的正切

asin(x)

arcsin x

x的反正弦

acos(x)

arcos x

x的反余弦

atan(x)

arctan x

x的反正切

log(x)

ln x

x的自然对数(以e为底)

log10(x)

Log10 x

x的常用对数(以10为底)

exp(x)

ex

e的x次方

ceil(x)

[x]

最小的>=x的整数

floor(x)

[x]

最大的<=x的整数

1.4 累积结果:阶乘

假设你有一个根汁饮料样品包,含有6种不同的根汁饮料。以不同的顺序喝各种口味可能会影响它们的味道。如果你希望尝试一切可能,有多少不同的顺序?结果答案是一个大得惊人的数字,720。你知道这个数字怎么来的吗?720是6的“阶乘”。

在数学中,阶乘通常用感叹号(!)表示,整数n的阶乘定义为n! = n(n – 1)(n – 2)……(1)。这恰好是n项的不同排列的数量。给定6个项,我们计算6! = (6)(5)(4)(3)(2)(1) = 720种可能的排列。

让我们编写一个程序,来计算用户输入数字的阶乘。程序的基本结构遵循“输入、处理、输出”模式:

输入要计算阶乘的数,n
计算n的阶乘,fact
输出fact

显然,这里棘手的是第二步。

实际如何计算阶乘?让我们手工尝试一下,以便得到处理的思路。在计算6的阶乘时,我们首先计算6(5) = 30。然后我们取该结果并做另一个乘法:30(4) = 120。这个结果乘以3:120(3) = 360。这个结果乘以2:360(2) = 720。根据定义,最后我们将这个结果乘以1,但不会改变最终值720。

现在让我们考虑更一般的算法。这里实际上发生了什么?我们正在做重复乘法,在做的过程中,我们记下得到的乘积。这是一种非常常见的算法模式,称为“累积器”。我们一步一步得到(或累积)最终的值。为了在程序中实现这一点,我们使用“累积器变量”和循环结构。一般模式如下:

初始化累积器变量
循环直到得到最终结果
更新累积器变量的值

意识到这是解决阶乘问题的模式,我们只需要填写细节。我们将累积阶乘。让我们把它保存在一个名为fact的变量中。每次通过循环,我们需要用fact乘以因子序列n、(n – 1)、……、1中的一个。看起来我们应该用一个for循环,迭代这个因子序列。例如,要计算6的阶乘,我们需要像这样工作的循环:

fact = 1
for factor in [6,5,4,3,2,1]:
  fact = fact * factor

请花一分钟跟踪这个循环的执行,并说服自己它有效。当循环体首次执行时,fact的值为1,因子为6。因此,fact的新值为1 * 6 = 6。下一次通过循环,因子将为5,fact更新为6 * 5 = 30。该模式对后续每个因子继续,直到累积得到最终结果720。

循环之前对fact赋初始值1,是循环开始所必需的。每次通过循环体(包括第一个),fact的当前值用于计算下一个值。初始化确保fact在第一次迭代时有一个值。每次使用累积器模式时,应确保包含正确的初始化。忘记这一点是新程序员的一个常见错误。

当然,我们还有很多其他的方法来编写这个循环。正如你从数学课上了解到的,乘法是可交换和结合的,所以执行乘法的顺序并不重要。我们可以很容易地走另一个方向。你可能还会注意到,在因子列表中包含1是不必要的,因为乘以1不会更改结果。下面是另一个版本,计算出相同的结果:

fact = 1
for factor in [2,3,4,5,6]:
  fact = fact * factor

不幸的是,这两个循环都不能解决原来的问题。我们手工编码了因子列表来计算6的阶乘。我们真正希望的是,一个可以计算任何给定输入n的阶乘的程序。我们需要某种方法,从n的值生成适当的因子序列。

好在用Python的range函数,这很容易做到。回想一下,range(n)产生一个数字序列,从0开始,增长到n,但不包括n。range有一些其他调用方式,可用于产生不同的序列。利用两个参数,range(start,n)产生一个以值start开始的序列,增长到n,但不包括n。第三个版本的range(start,n,step)类似于双参数版本,但它使用step作为数字之间的增量。下面有一些例子:

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(5,10))
[5, 6, 7, 8, 9]
>>> list(range(5, 10, 3))
[5, 8]

给定输入值n,我们有几种不同的range命令,能产生适当的因子列表,用于计算n的阶乘。为了从最小到最大生成它们(我们的第二种循环),我们可以使用range(2,n + 1)。注意,我使用n + 1作为第二个参数,因为范围将上升到n + 1,但不包括此值。我们需要加1来确保n本身被包括,作为最后一个因子。

另一种可能是使用三参数版本的range和负数步长,产生另一个方向(我们的第一种循环)的因子,导致倒计数:range(n,1,−1)。这个循环产生一个列表,从n开始并向下计数(step为−1)到1,但不包括1。

下面是一种可能的阶乘程序版本:

# factorial.py
#  Program to compute the factorial of a number
#  Illustrates for loop with an accumulator
def main():
  n = int(input("Please enter a whole number: "))
  fact = 1
  for factor in range(n,1,-1):
    fact = fact * factor
  print("The factorial of", n, "is", fact)
main()

当然,写这个程序还有很多其他方法。我已经提到改变因子的顺序。另一种可能是将fact初始化为n,然后使用从n−1开始的因子(只要n> 0)。你可以尝试这样一些变化,看看你最喜欢哪一个。

1.5 计算机算术的局限性

有时我们会联想到,用“!”表示阶乘是因为该函数增长非常快。例如,下面是用我们的程序求100的阶乘:

Please enter a whole number: 100
The factorial of 100 is 9332621544394415268169923885626670049071596826
43816214685929638952175999932299156089414639761565182862536979208272237
58251185210916864000000000000000000000000

这是一个相当大的数字!

尽管最新版本的Python对此计算没有困难,但是旧版本的Python(以及其他语言的现代版本,例如C ++和Java)不会如此。例如,下面是使用Java编写的类似程序的几次运行:

# run 1
Please enter a whole number: 6
The factorial is: 720
# run 2
Please enter a whole number: 12
The factorial is: 479001600
# run 3
Please enter a whole number: 13
The factorial is: 1932053504

这看起来不错。我们知道6! = 720。快速检查也确认12! = 479001600。遗憾的是,事实证明,13! = 6227020800。看起来Java程序给出了不正确的答案!

这里发生了什么?到目前为止,我们已经讨论了数值数据类型作为熟悉数字的表示,例如整数和小数(分数)。然而,重要的是要记住,数字的计算机表示(实际数据类型)并不总是表现得像它们所代表的数字那样。

在第1章中你了解到,计算机的CPU可以执行非常基本的操作,如两个数字相加或相乘,还记得吗?更准确地说,CPU可以对计算机的数字的内部表示执行基本操作。这个Java程序的问题是它使用计算机的底层int数据类型来表示整数,并依赖于计算机对int的乘法运算。不幸的是,这些机器int不完全像数学整数。有无穷多个整数,但int的范围是有限的。在计算机内部,int以固定大小的二进制表示存储。为了理解这一切,我们需要了解硬件层面发生了什么。

计算机存储器由电“开关”组成,每个开关可以处于两种可能状态之一,即开或关。每个开关表示一个二进制数字的信息,称为“位”。一位可以编码两种可能性,通常用数字0(关闭)和1(打开)表示。位序列可以用于表示更多的可能性。用两位,我们可以表示四件事:

bit 2 bit 1
0   0
0   1
1   0
1   1

三位允许我们通过对四个两位模式中的每一个添加0或1,来表示八个不同的值:

bit 3 bit 2 bit 1
0   0   0
0   0   1
0   1   0
0   1   1
1   0   0
1   0   1
1   1   0
1   1   1

你可以看到这里的模式。每增加一位让不同模式的数量加倍。通常,n位可以表示2n个不同的值。

特定计算机用来表示int的位数取决于CPU的设计。现在,典型的PC使用32或64位。对于32位CPU,这意味着有232个可能的值。这些值以0为中心,表示正整数和负整数的范围。现在232/2 = 231。因此,可以在32位int值中表示的整数范围是−231到231 − 1。在上限−1的原因是考虑在范围的上半部分中的0的表示。

有了这个知识,让我们试着理解Java阶乘例子中发生的事情。如果Java程序依赖于32位int表示,它可以存储的最大数字是多少?Python可以给我们一个快速的答案:

>>> 2**31-1
2147483647

注意,这个值(约21亿)在12!(约4.8亿)和13!(约62亿)之间。这意味着Java程序可以很好地计算到12的阶乘,但是之后,表示“溢出”,结果是垃圾。现在你知道了为什么简单的Java程序不能计算13!。当然,这给我们留下了另一个谜题。为什么现代Python程序似乎能很好地用大整数计算?

首先,你可能认为Python使用浮点数据类型来绕过int的大小限制。然而,事实证明,浮点数并没有真正解决这个问题。下面是使用浮点数的修改后的阶乘程序的示例运行:

Please enter a whole number: 30
The factorial of 30 is 2.6525285981219103e+32

虽然这个程序运行很好,但切换到浮点数后,我们不再能得到确切的答案。

非常大(或非常小)的浮点值使用“指数”的方式打印,称为“科学记数法”。结束时的e + 32表示结果等于2.6525285981219103 * 1032。你可以把+32作为一个标记,表示小数点的位置。在这个例子中,它必须向右移动32个位置以获取实际值。但是,小数点右边只有16位数字,因此我们已经“丢失”了最后16位数字。

使用浮点数,我们可以表示比32位int更大的“范围”的值,但“精度”仍然是固定的。事实上,计算机将浮点数保存为一对固定长度(二进制)整数。一个整数称为“尾数”,表示值中的数字串,第二个称为“指数”,记录整数部分结束和小数部分开始的位置(“二进制小数点”在哪里)。回忆一下,我告诉过你浮点是近似值。现在你可以看到原因。因为底层数字是二进制的,所以只有涉及2的幂的分数可以被精确地表示。任何其他分数产生无限重复的尾数。(就像1/3产生无限重复的十进制,因为3不是10的幂)。当无限长的尾数被截断到固定长度以进行存储时,结果是近似的。用于尾数的位数决定了近似值的精确程度,但绕不过它们是近似的事实。

幸运的是,Python对于大的、精确的值有一个更好的解决方案。Python的int不是固定的大小,而是扩展到适应任何值。唯一的限制是计算机可用的内存量。当值很小时,Python就用计算机的底层int表示和操作。当值变大时,Python会自动转换为使用更多位的表示。当然,为了对更大的数字执行操作,Python必须将操作分解为计算机硬件能够处理的更小的单元,类似于你手工计算长除法的方式。这些操作不会那么有效(它们需要更多的步骤),但是它们允许Python的int增长到任意大小。这就是为什么我们的简单阶乘程序可以计算一些大的结果。这是一个非常酷的Python特性。

1.6 小结

本文介绍了一些有关进行数值计算的程序的重要细节。下面是一些关键概念的快速摘要。

  • 计算机表示特定类型的信息的方式称为数据类型。对象的数据类型决定了它可以具有的值和它支持的操作。

  • Python有几种不同的数据类型来表示数值,包括int和float。

  • 整数通常使用int数据类型表示,小数值使用float表示。所有Python数字数据类型都支持标准的内置数学运算:加法(+)、减法(−)、乘法(*)、除法(/),整除(//),取余(%)和绝对值(abs(x))。

  • Python在某些情况下,自动将数字从一种数据类型转换为另一种。例如,在涉及int和float的混合类型表达式中,Python先将int转换为float,然后使用浮点运算。

  • 程序还可以使用函数float()、int()和round()将一个数据类型显式转换为另一个数据类型。通常应该使用类型转换函数代替eval来处理用户的数字输入。

  • 其他数学函数在math库中定义。要使用这些功能,程序必须首先导入该库。

  • 数值结果通常通过计算值序列的和或积来计算。循环累积器编程模式对于这种计算很有用。

  • int和float在底层计算机上都使用固定长度的位序列表示。这让这些表示有某些限制。在32位的机器上,硬件int必须在−231~231−1中。浮点数的精度有限,不能精确地表示大多数数字。

  • Python的int数据类型可以用于存储任意大小的整数。如果int值对于底层硬件int太大,就会自动转换为更长的表示。涉及这些长int的计算比只使用短int的计算效率低。

1.7 练习

复习问题

判断对错

1.由计算机存储和操作的信息称为数据。

2.由于浮点数是非常准确的,所以通常应该使用它们,而不是int

3.像加法和减法这样的操作在math库中定义。

4.n项的可能排列的数目等于n!。

5.sqrt函数计算数字的喷射(squirt)。

6.float数据类型与实数的数学概念相同。

7.计算机使用二进制表示数字。

8.硬件float可以表示比硬件int更大范围的值。

9.在获取数字作为用户输入时,类型转换函数(如float)是eval的安全替代。

10.在Python中,4 + 5产生与4.0 + 5.0相同的结果类型。

多项选择

1.下列    项不是内置的Python数据类型。

a.int

b.float

c.rational

d.string

2.以下    项不是内置操作。

a.+

b.%

c.abs()

d.sqrt()

3.为了使用math库中的函数,程序必须包括    

a.注释

b.循环

c.操作符

d.import语句

4.4!的值是    

a.9

b.24

c.41

d.120

5.用于存储π的值,最合适的数据类型是    

a.int

b.float

c.irrational

d.string

6.可以使用5位比特表示的不同值的数量是    

a.5

b.10

c.32

d.50

7.在包含int和float的混合类型表达式中,Python会进行的转换是    

a.浮点数到整数

b.整数到字符串

c.浮点数和整数到字符串

d.整数到浮点数

8.下列    项不是Python类型转换函数。

a.float

b.round

c.int

d.abs

9.用于计算阶乘的模式是    

a.累积器

b.输入、处理、输出

c.计数循环

d.格子

10.在现代Python中,int值大于底层硬件int时,会    

a.导致溢出

b.转换为float

c.打破计算机

d.使用更多的内存

讨论

1.显示每个表达式求值的结果。确保该值以正确的形式表示其类型(int或float)。如果表达式是非法的,请解释为什么。

a.4.0 / 10.0 + 3.5 * 2

b.10 % 4 + 6 / 2

b.abs(4 - 20 // 3) ** 3

d.sqrt(4.5 - 5.0) + 7 * 3

e.3 * 10 // 3 + 10 % 3

f.3 ** 3

2.将以下每个数学表达式转换为等效的Python表达式。你可以假定math库已导入(通过import math)。

a.(3 + 4)(5)

b.

c.4πr2

d.

e.

3.显示将由以下每个range表达式生成的数字序列。

a.range(5)

b.range(3, 10)

c.range(4, 13, 3)

d.range(15, 5, -2)

e.range(5, 3)

4.显示以下每个程序片段产生的输出。

a.

for i in range(1, 11):
  print(i*i)

b.

for i in [1,3,5,7,9]:
  print(i, ":", i**3)
print(i)

c.

x = 2
y = 10
for j in range(0, y, x):
  print(j, end="")
  print(x + y)
print("done")

d.

ans = 0
for i in range(1, 11):
  ans = ans + i*i
  print(i)
print (ans)

5.如果使用负数作为round函数中的第二个参数,你认为会发生什么?例如,round(314.159265,−1)的结果应该是什么?请解释答案的理由。在你写下答案后,请参阅Python文档或尝试一些例子,看看Python在这种情况下实际上做了什么。

6.当整数除法或余数运算的操作数为负数时,你认为会发生什么?考虑以下每种情况并尝试预测结果。然后在Python中试试。(提示:回顾一下神奇的公式a = (a//b)(b) + (a%b)。)

a.−10 // 3

b.−10 % 3      

c.10 // −3

d.10 % −3

e.−10 // −3

编程练习

1.编写一个程序,利用球体的半径作为输入,计算体积和表面积。以下是一些可能有用的公式:

V = 4/3πr3

A = 4πr2

2.给定圆形比萨饼的直径和价格,编写一个程序,计算每平方英寸的成本。面积公式为A = πr2

3.编写一个程序,该程序基于分子中的氢、碳和氧原子的数量计算碳水化合物的分子量(以克/摩尔计)。程序应提示用户输入氢原子的数量、碳原子的数量和氧原子的数量。然后程序基于这些单独的原子量打印所有原子的总组合分子量。

原子质量(克/摩尔)

H

1.00794

C

12.0107

O

15.9994

例如,水(H2O)的分子量为2(1.00794)+ 15.9994 = 18.01528。

4.编写一个程序,根据闪光和雷声之间的时间差来确定雷击的距离。声速约为1100英尺/秒,1英里为5280英尺。

5.Konditorei咖啡店售卖咖啡,每磅10.50美元加上运费。每份订单的运费为每磅0.86美元 +固定成本1.50美元。编写计算订单费用的程序。

6.使用坐标(x1,y1)和(x2,y2)指定平面中的两个点。编写一个程序,计算通过用户输入的两个(非垂直)点的直线的斜率。

{-:-}斜率 = 

7.编写一个程序,接受两点(见上一个问题),并确定它们之间的距离。

{-:-}距离 = 

8.格里高利闰余是从1月1日到前一个新月的天数。此值用于确定复活节的日期。它由下列公式计算(使用整型算术):

C = year//100

epact = (8 + (C//4) - C + ((8C + 13)//25) + 11(year%19))%30

编写程序,提示用户输入4位数年份,然后输出闰余的值。

9.使用以下公式编写程序以计算三角形的面积,其三边的长度为a、b和c:

{-:-}

10.编写程序,确定梯子斜靠在房子上时,达到给定高度所需的长度。梯子的高度和角度作为输入。计算长度使用公式为:

注意:角度必须以弧度表示。提示输入以度为单位的角度,并使用以下公式进行转换:

11.编程计算前n个自然数的和,其中n的值由用户提供。

12.编程计算前n个自然数的立方和,其中n的值由用户提供。

13.编程对用户输入的一系列数字求和。 程序应该首先提示用户有多少数字要求和,然后依次提示用户输入每个数字,并在输入所有数字后打印出总和。(提示:在循环体中使用输入语句。)

14.编程计算用户输入的一系列数字的平均值。与前面的问题一样,程序会首先询问用户有多少个数字。注意:平均值应该始终为float,即使用户输入都是int。

15.编写程序,通过对这个级数的项进行求和来求近似的π值:4/1 – 4/3 + 4/5 – 4/7 + 4/9 − 4/11 +……程序应该提示用户输入n,要求和的项数,然后输出该级数的前n个项的和。让你的程序从math.pi的值中减去近似值,看看它的准确性。

16.斐波那契序列是数字序列,其中每个连续数字是前两个数字的和。经典的斐波那契序列开始于1,1,2,3,5,8,13,……。编写计算第n个斐波纳契数的程序,其中n是用户输入的值。例如,如果n = 6,则结果为8。

17.你已经看到math库包含了一个计算数字平方根的函数。在本练习中,你将编写自己的算法来计算平方根。解决这个问题的一种方法是使用猜测和检查。你首先猜测平方根可能是什么,然后看看你的猜测是多么接近。你可以使用此信息进行另一个猜测,并继续猜测,直到找到平方根(或其近似)。一个特别好的猜测方法是使用牛顿法。假设x是我们希望的根,guess是当前猜测的答案。猜测可以通过使用计算下一个猜测来改进:

编程实现牛顿方法。程序应提示用户找到值的平方根(x)和改进猜测的次数。从猜测值x / 2开始,你的程序应该循环指定的次数,应用牛顿的方法,并报告猜测的最终值。你还应该从math.sqrt(x)的值中减去你的估计值,以显示它的接近程度。


本文摘自《Python程序设计(第3版)》


Python之父作序推荐 

Python编程入门经典

 编辑推荐: 

● 广泛使用计算机图形学——本书提供一个简单的图形软件包graphics.py作为示例。 

● 生动有趣的例子——本书包含了完整的编程示例来解决实际问题。 

● 亲切自然的行文——以自然的叙事风格介绍了重要的计算机科学概念。 

● 灵活的螺旋式学习过程——简单地呈现概念,逐渐介绍新的思想,章节末加以巩固强化。 

● 时机恰好地介绍对象——本书既不是严格的“早讲对象”,也不是“晚讲对象”,而是在命令式编程 的基础上简要地介绍了对象概念。 

● 提供丰富的教学素材——提供了大量的章末习题。还提供代码示例和教学PPT下载。 


本书以Python语言为工具教授计算机程序设计。本书强调解决问题、设计和编程是计算机科学的核心技能。本书特色鲜明、示例生动有趣、内容易读易学,适合Python入门程序员阅读,也适合高校计算机专业的教师和学生参考。 


作者简介 

John Zelle是美国Wartburg大学数学和计算机系教授。他负责教授Python程序设计课程,并且结合多年的教学经验编写了本书,在美国高校受到普遍的欢迎。他还从事VR、AI等方面的研究,发表了一些机器学习方面的论文。 


小福利


关注【异步社区】服务号,转发本文至朋友圈或 50 人以上微信群,截图发送至异步社区服务号后台,并在文章底下留言,分享你的Python开发经验或者本书的试读体验,我们将选出3名读者赠送《Python程序设计(第3版)》1本赶快积极参与吧!

活动截止时间:2018 年2月4日

上期获奖名单 

箫山茔和 {-_-}

请获奖读者填写下方获奖信息,活动名称异步社区 Linux二进制分析https://www.wenjuan.in/s/m2iaqif/

异步社区”后台回复“关注”,即可免费获得2000门在线视频课程;推荐朋友关注根据提示获取赠书链接,免费得异步图书一本。赶紧来参加哦!

扫一扫上方二维码,回复“关注”参与活动!

阅读原文,购买《Python程序设计(第3版)》


  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值