《Python完全自学教程》免费在线连载3.3.2

2022-04-01 10:03:09 浏览数 (1)

3.3.2 标准库的数学模块

Python 的发明者吉多·范罗苏姆说:Python 有“自带电池”的理念,从它的庞大软件包复杂而又可靠的能力中可见端倪(英文:Python has a "batteries included" philosophy. This is best seen through the sophisticated and robust capabilities of its larger packages.参阅:https://en.wikipedia.org/wiki/Standard_library)。所谓“自带电池”就是指 Python 标准库(Python Standard Library,官方文档地址是 https://docs.python.org/3/library/index.html),标准库的有关程序在安装本地 Python 开发环境时已经随之安装好,与内置函数类似,也能“开箱即用”。

Python 标准库非常庞大,此处仅介绍与初等数学计算相关的模块(更多内容,参阅第11章11.3节)。

1. math 模块

标准库中的 math 模块主要提供初等数学中常用函数,官方文档地址是 https://docs.python.org/3/library/math.html。请在本地交互模式中,输入 import math ,然后敲回车键,如下所示:

代码语言:javascript复制
>>> import math    # (1)
>>>

输入注释(1)并敲回车键后,光标停在了下一行,且没有任何返回内容——关键是没有报错,这说明本地已经正确安装了 math 模块。注释(1)的作用是将标准库中的 math 模块引入到当前环境——标准库不是内置函数,其模块都必须用关键词 import 引入之后才能使用(参阅第11章11.1节)。

接着注释(1),继续执行如下操作:

代码语言:javascript复制
>>> dir(math)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'lcm', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'nextafter', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc', 'ulp']

这里使用了内置函数 dir() ,它能够返回参数对象的属性和方法——在第2章2.4节初步接触了对象的“属性”和“方法”,更详细的内容请参阅第8章。对于 dir(math) 的返回结果,也可以理解为模块 math 里面提供的函数。不妨浏览一番这些函数名称,并与记忆中的初等数学常用函数对比,是不是觉得这里已经基本涵盖了常用的函数呢?

这些函数怎么用?以余弦函数 cos 为例,根据自学经验,应该先看一看这个函数的文档:

代码语言:javascript复制
>>> help(math.cos)

注意上述写法,不能直接写 help(cos) ,因为函数 cos 是模块 math 的一员,注释(1)引入的是这个模块,当调用其中的函数时,必须借助模块的名称(可用图3-3-4所示帮助记忆和理解。这是调用模块内函数的一种方式,更多内容参阅第11章11.1节)。

图3-3-4 调用模块里的函数

执行上述语句,可以看到 math.cos 的帮助文档:

代码语言:javascript复制
cos(x, /)
    Return the cosine of x (measured in radians).
代码语言:javascript复制
>>> alpha = 60 / 180 * math.pi    # (2)
>>> math.cos(alpha)               # (3)
0.5000000000000001
代码语言:javascript复制
>>> math.pi
3.141592653589793
代码语言:javascript复制
>>> round(math.cos(math.pi/4)   math.log10(5) * math.exp(2), 2)
5.87

2. fractions 模块

在前面的代码演示中,注释(2)只能将分数计算为浮点数实现近似计算,如何才能实现精确地表示

frac{60}{180}=frac{1}{3}

呢?这就是标准库中的 fractions 模块所要解决的问题。

代码语言:javascript复制
>>> import fractions                  # (4)
>>> a = fractions.Fraction(60, 180)   # (5)
>>> a
Fraction(1, 3)

注释(4)引入模块 fractions ,注释(5)使用 fractions.Fraction() 创建分数——注意大小写,其参数中的第一个 60 是分数的分子,第二个 180 是分数的分母,即

frac{60}{180}

。返回值是 Fraction(1, 3) ,表示分数

frac{1}{3}

。由此可见,注释(5)创建分数的时候,会自动化简,以最简分数作为结果。

代码语言:javascript复制
>>> 1 / 3   1 / 2
0.8333333333333333

这是用浮点数形式计算

frac{1}{3} frac{1}{2}

的结果,现在可以这样做:

代码语言:javascript复制
>>> b = fractions.Fraction(1, 2)
>>> a   b
Fraction(5, 6)

如此就实现了分数的精确计算。

除了用注释(5)创建分数之外,还有其他的创建方式,此处不再赘述,请读者自行参考官方文档(https://docs.python.org/3/library/fractions.html)中的示例。下面要对注释(2)和(3)重新计算,用分数得到精确结果。

代码语言:javascript复制
>>> alpha = fractions.Fraction('60/180') * math.pi            # (6)
>>> fractions.Fraction(math.cos(alpha)).limit_denominator()   # (7)
Fraction(1, 2)

先看结果,得到了

cosleft(frac{1}{3}piright)

的精确结果

frac{1}{2}

。不过,注释(6)中使用了另外一种创建分数的方法——参数形式与(5)不同(建议读者参阅官方文档示例)。注释(7)比较长,可以分为两部分,第一部分是 fractions.Fraction(math.cos(alpha)) ,这其实是第三种创建分数的方法——参数是浮点数。

代码语言:javascript复制
>>> fractions.Fraction(0.5)
Fraction(1, 2)

如注释(3)所示,math.cos(alpha)的值是一个浮点数,再以它为参数,创建分数:

代码语言:javascript复制
>>> fractions.Fraction(math.cos(alpha))    # (8)
Fraction(4503599627370497, 9007199254740992)

注意,math.cos(alpha) 的浮点数结果并非严格等于 0.5(如注释(3)的返回值所示),因此在注释(8)中看到所得分数只能很接近于 Fraction(1, 2) 。为此,注释(7)调用了该对象的方法 limit_denominator() ,它的作用是返回最近似的分数——需要注意此方法的默认参数 max_denominator=1000000 ,最近似的分数是以此分母计算,例如:

代码语言:javascript复制
>>> fractions.Fraction('3.1415926535897932').limit_denominator(1000)
Fraction(355, 113)
>>> fractions.Fraction('3.1415926535897932').limit_denominator(100)
Fraction(311, 99)
>>> fractions.Fraction('3.1415926535897932').limit_denominator(10)
Fraction(22, 7)

当然,此处所创建的分数,也可以和浮点数、整数进行算术运算,或者作为函数的参数。

代码语言:javascript复制
>>> a
Fraction(1, 3)
>>> a   2
Fraction(7, 3)
>>> a   0.5
0.8333333333333333
>>> a ** 2
Fraction(1, 9)
>>> pow(a, 3)
Fraction(1, 27)
>>> math.sin(a)
0.3271946967961522

问题来了,既然用分数对象能够精确计算,还要浮点数有什么用?有用!在 Python 中,运算 float 类型的对象要比运算 fractions.Fraction 类型的对象速度快,并且在一般情况下,浮点数运算的精度已经足够了。如果不是“一般情况”呢?就“鱼和熊掌不可兼得”,只能忍受“速度慢”了吗?也不是。Python 是一个开放的生态系统,除了标准库之外,还有更庞大的“第三方库”(参阅第11章),其中就有解决此问题的模块,比如 quicktions —— “日光之下,并无新事”,倘若真的找不到满足需要的工具,那就是创新的机遇,一定要抓住。

针对本节内容,建议读者自己设计一些计算题进行练习——可以帮助中小学生解题。

这里暂介绍标准库中与数学计算有关的两个模块,在后续学习中还会遇到其他模块,第11章就此有专门介绍。

自学建议 本节的学习中,使用了“帮助文档”和“官方文档”,这些文档是关于编程语言的最权威资料。但是,如何使用这些文档,是一个需要探讨的问题。以下是我的个人经验,供读者参考:

  • 将文档当做《新华字典》那样的工具,有问题时去查询。一般地,语文学科的主要学习资料是课本,字典是必不可少的辅助学习的工具。但很少有人用《新华字典》来学习语文。有人号称“看文档就能掌握编程语言”,他一定是聪明人。
  • 如果文档的原初语言是英文,尽可能看英文的文档(虽然有的文档翻译为中文了,但有的翻译并没有体现本意)。在第1章1.6节的【自学建议】中已经就本书引用英文文档的问题给予了解释说明,这里再次强调,旨在避免读者误解。凡是有志于自学,并以成为某领域翘楚为目标者,均不会畏惧各类文档中的英文。

在本书后续内容,我会经常提醒读者查看文档,根据以往经验,会有的学习者对此破不耐烦——曾有学习者在我的课程中对这种建议给予了“狂喷”。但我不会由于他的不满而改变此风格,真正有追求的读者能理解我的建议和要求。”

0 人点赞