《Python编程的术与道:Python语言入门》视频课程
《Python编程的术与道:Python语言入门》视频课程链接:https://edu.csdn.net/course/detail/27845
模块 (Modules)
Python中的大多数功能都是由modules提供的。 Python标准库是大量模块的集合。
导入模块
要在Python程序中使用模块,首先必须将其导入。 可以使用import
语句导入模块。
例如,要导入包含许多标准数学函数的模块math
,我们可以执行以下操作:
import math
这包括整个模块,并使其可在程序中稍后使用。
import math
x = math.cos(2 * math.pi)
print(x)
1.0
另外,我们可以选择将模块中的部分或全部符号(函数和变量)导入到当前命名空间中(这样,当我们每次使用来自math
模块中的内容时,我们都不需要使用前缀math
。
from math import cos, pi
x = cos(2 * pi)
print(x)
1.0
这称为 selective Import(选择性导入)。
这种模式可能非常方便,但是在包含许多模块的大型程序中,通常最好使用import math
模式将每个模块中的符号保留在自己的命名空间中。 这将消除命名空间冲突可能引起混淆的问题。
另外, in case of namespace collisions
(or to avoid namespace pollution
) we may use the as
关键字。
from math import cos as cosine # Now the `cos` function can be referenced as `cosine`
cosine(math.pi/2)
6.123233995736766e-17
最后,如果我们想从模块中导入所有内容,则可以使用*
符号:
from math import *
print("Cosine Function: ", cos(pi))
print("Sin Function: ", sin(pi))
print("Logarithm: ", log(e))
print("Power function: ", pow(3, 3))
Cosine Function: -1.0
Sin Function: 1.2246467991473532e-16
Logarithm: 1.0
Power function: 27.0
查看模块包含的内容及其文档
导入模块后, 可以使用dir
函数列出其提供的符号:
import math
print(dir(math))
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
使用函数help
,我们可以得到每个函数的描述
help(math.log)
Help on built-in function log in module math:
log(...)
log(x, [base=math.e])
Return the logarithm of x to the given base.
If the base not specified, returns the natural logarithm (base e) of x.
导入如何工作 (How Import Works)
由于导入是Python程序结构的核心,因此本节将对导入操作进行更为正式的详细介绍。
在Python中,导入过程不只是将一个文件文本插入另一个文件中。
它实际上由一组运行时操作组成,它们在程序首次导入给定文件时执行三个不同的步骤:
- **查找(Find)**模块文件
- **编译 (Compile)**为字节码
- **运行 (Run)**模块的代码以创建其定义的对象
1. Find It
首先,Python必须找到代码正在尝试导入的模块文件。
为此,Python应用了适当的模块搜索路径算法。 特别是,Python在以下目录中按以下顺序查找要导入的模块:
1.程序的主目录
2.PYTHONPATH
目录
3.标准库目录
4.任何.pth
文件的内容(如果存在)
5.第三方扩展程序的site-packages
主目录(例如,/usr/lib/python3/site-packages/
)
2. Compile it
通过遍历模块搜索路径找到与import语句匹配的源代码文件后,Python如有必要,接下来将其编译为字节码。
特别是,在此步骤中,两个选择是:
- 编译:
如果字节码文件早于源文件(即,如果你更改了源代码)或由其他Python版本创建,则在程序运行时Python会自动重新生成字节代码。
- 不编译:
另一方面,如果Python找到的.pyc
字节代码文件不早于相应的.py
源文件并且是由相同的Python版本创建的,则它会跳过源代码到字节代码的编译步骤。
此外,如果Python在搜索路径上仅找到一个字节码文件而没有任何源码,它只是直接加载该字节代码。
这意味着你可以仅将字节代码文件形式发布程序,而不必发送源代码。
换句话说,如果可能的话,编译步骤是可以“绕过”的,以加快程序启动的速度。
3. Run it
导入操作的最后一步将执行模块的字节码。
文件中的所有语句从上到下依次运行,并且在此步骤中对名称所做的任何名称都会生成结果模块对象的属性。
写模块 (Writing modules)
Python中的模块只是扩展名为.py
的Python文件。 模块的名称将是文件的名称。 Python模块可以具有一组定义和实现的函数、类或变量。 在示例中,我们将有两个文件:
mygame/
mygame/game.py
mygame/draw.py
模块初始化 (Module initialization)
第一次将模块加载到一个正在运行的Python脚本中时,只需执行一次模块中的代码即可将其初始化。 如果你的代码中的另一个模块再次导入相同的模块,则不会加载两次,而只会加载一次。模块内的局部变量仅初始化一次
包 (package)
目录中必须包含一个名为__init__.py
的文件,Python才能将其视为一个包。 该文件可以为空,但是通常将该程序包的初始化代码放入此文件中。
下面是一个例子。 假设我们正在开发游戏,则可能的包和模块组织如下图所示。
从包中导入模块
我们可以使用点运算符.
从包中导入模块。
例如,如果要导入start模块,可用:
import Game.Level.start
如果该模块包含名为select_difficulty()
的函数,则必须使用全名来引用它。
Game.Level.start.select_difficulty(2)
这个构造看起来很冗长,可以按以下方式导入不带包前缀的模块。
from Game.Level import start
现在,我们可以简单地如下调用该函数。
start.select_difficulty(2)
仅将所需的函数(或类或变量)从包中的模块导入模块的另一种方式如下。
from Game.Level.start import select_difficulty
现在我们可以直接调用此函数。
select_difficulty(2)
尽管这样比较简单,但使用完整的名称空间可避免混淆,并防止两个相同的标识符名称冲突。
写包 (Writing packages)
包是命名空间,它们本身包含多个包和模块。 它们虽然是目录,但还是有所不同。
Python中的每个软件包都是一个目录,必须包含一个名为__init__.py
的特殊文件。 该文件可以为空,表示该文件所在的目录是Python软件包,因此可以与导入模块相同的方式进行导入。
如果创建一个名为foo的目录,该目录标记了程序包名称,则可以在该程序包中创建一个名为bar的模块。 我们也不要忘记在foo目录中添加__init__.py
文件。
__init__.py
文件还可以通过覆盖__all__
变量来确定软件包将哪些模块导出为API,同时将其他模块保留在内部,如下所示:
__init__.py
:
__all__ = ["bar"]