Files
team-learning-program/PythonLanguage/15. 模块.md
T
2020-07-31 17:39:54 +08:00

369 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 模块
在前面我们脚本是用 Python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。
为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块(Module)。
模块是一个包含所有你定义的函数和变量的文件,其后缀名是`.py`。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 Python 标准库的方法。
---
## 1. 什么是模块
- 容器 -> 数据的封装
- 函数 -> 语句的封装
- 类 -> 方法和属性的封装
- 模块 -> 程序文件
【例子】创建一个 hello.py 文件
```python
# hello.py
def hi():
print('Hi everyone, I love lsgogroup!')
```
---
## 2. 命名空间
命名空间因为对象的不同,也有所区别,可以分为如下几种:
- 内置命名空间(Built-in Namespaces):Python 运行起来,它们就存在了。内置函数的命名空间都属于内置命名空间,所以,我们可以在任何程序中直接运行它们,比如`id()`,不需要做什么操作,拿过来就直接使用了。
- 全局命名空间(ModuleGlobal Namespaces):每个模块创建它自己所拥有的全局命名空间,不同模块的全局命名空间彼此独立,不同模块中相同名称的命名空间,也会因为模块的不同而不相互干扰。
- 本地命名空间(Function & ClassLocal Namespaces):模块中有函数或者类,每个函数或者类所定义的命名空间就是本地命名空间。如果函数返回了结果或者抛出异常,则本地命名空间也结束了。
上述三种命名空间的关系
![](https://img-blog.csdnimg.cn/20181228200005502)
程序在查询上述三种命名空间的时候,就按照从里到外的顺序,即:Local Namespaces --> Global Namesspaces --> Built-in Namesspaces。
【例子】
```python
import hello
hello.hi() # Hi everyone, I love lsgogroup!
hi() # NameError: name 'hi' is not defined
```
---
## 3. 导入模块
【例子】创建一个模块 TemperatureConversion.py
```python
# TemperatureConversion.py
def c2f(cel):
fah = cel * 1.8 + 32
return fah
def f2c(fah):
cel = (fah - 32) / 1.8
return cel
```
- 第一种:import 模块名
【例子】
```python
import TemperatureConversion
print('32摄氏度 = %.2f华氏度' % TemperatureConversion.c2f(32))
print('99华氏度 = %.2f摄氏度' % TemperatureConversion.f2c(99))
# 32摄氏度 = 89.60华氏度
# 99华氏度 = 37.22摄氏度
```
- 第二种:from 模块名 import 函数名
【例子】
```python
from TemperatureConversion import c2f, f2c
print('32摄氏度 = %.2f华氏度' % c2f(32))
print('99华氏度 = %.2f摄氏度' % f2c(99))
# 32摄氏度 = 89.60华氏度
# 99华氏度 = 37.22摄氏度
```
下面的方式不推荐
【例子】
```python
from TemperatureConversion import *
print('32摄氏度 = %.2f华氏度' % c2f(32))
print('99华氏度 = %.2f摄氏度' % f2c(99))
# 32摄氏度 = 89.60华氏度
# 99华氏度 = 37.22摄氏度
```
- 第三种:import 模块名 as 新名字
【例子】
```python
import TemperatureConversion as tc
print('32摄氏度 = %.2f华氏度' % tc.c2f(32))
print('99华氏度 = %.2f摄氏度' % tc.f2c(99))
# 32摄氏度 = 89.60华氏度
# 99华氏度 = 37.22摄氏度
```
---
## 4. `if __name__ == '__main__'`
对于很多编程语言来说,程序都必须要有一个入口,而 Python 则不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。
假设我们有一个 const.py 文件,内容如下:
```python
PI = 3.14
def main():
print("PI:", PI)
main()
# PI: 3.14
```
现在,我们写一个用于计算圆面积的 area.py 文件,area.py 文件需要用到 const.py 文件中的 `PI` 变量。从 const.py 中,我们把 `PI` 变量导入 area.py
```python
from const import PI
def calc_round_area(radius):
return PI * (radius ** 2)
def main():
print("round area: ", calc_round_area(2))
main()
'''
PI: 3.14
round area: 12.56
'''
```
我们看到 const.py 中的 main 函数也被运行了,实际上我们不希望它被运行,因为 const.py 提供的 main 函数只是为了测试常量定义。这时`if __name__ == '__main__'`派上了用场,我们把 const.py 改一下,添加`if __name__ == "__main__"`
```python
PI = 3.14
def main():
print("PI:", PI)
if __name__ == "__main__":
main()
```
运行 const.py,输出如下:
```python
PI: 3.14
```
运行 area.py,输出如下:
```python
round area: 12.56
```
`__name__`:是内置变量,可用于表示当前模块的名字。
```python
import const
print(__name__)
# __main__
print(const.__name__)
# const
```
由此我们可知:如果一个 .py 文件(模块)被直接运行时,其`__name__`值为`__main__`,即模块名为`__main__`
所以,`if __name__ == '__main__'`的意思是:当 .py 文件被直接运行时,`if __name__ == '__main__'`之下的代码块将被运行;当 .py 文件以模块形式被导入时,`if __name__ == '__main__'`之下的代码块不被运行。
---
## 5. 搜索路径
当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。
【例子】
```python
import sys
print(sys.path)
# ['C:\\ProgramData\\Anaconda3\\DLLs', 'C:\\ProgramData\\Anaconda3\\lib', 'C:\\ProgramData\\Anaconda3', 'C:\\ProgramData\\Anaconda3\\lib\\site-packages',...]
```
<u>我们使用 import 语句的时候,Python 解释器是怎样找到对应的文件的呢?</u>
这就涉及到 Python 的搜索路径,搜索路径是由一系列目录名组成的,Python 解释器就依次从这些目录中去寻找所引入的模块。
这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。
搜索路径是在 Python 编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在 `sys` 模块中的 `path` 变量中。
---
## 6. 包(package
包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。
创建包分为三个步骤:
- 创建一个文件夹,用于存放相关的模块,文件夹的名字即包的名字。
- 在文件夹中创建一个 `__init__.py` 的模块文件,内容可以为空。
- 将相关的模块放入文件夹中。
不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。
现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,.aiff,.au),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。
并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。
这里给出了一种可能的包结构(在分层的文件系统中):
```python
sound/ 顶层包
__init__.py 初始化 sound
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ filters 子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
```
在导入一个包的时候,Python 会根据 `sys.path` 中的目录来寻找这个包中包含的子目录。
目录只有包含一个叫做 `__init__.py` 的文件才会被认作是一个包,最简单的情况,放一个空的 `__init__.py` 就可以了。
```python
import sound.effects.echo
```
这将会导入子模块 `sound.effects.echo`。 他必须使用全名去访问:
```python
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
```
还有一种导入子模块的方法是:
```python
from sound.effects import echo
```
这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:
```python
echo.echofilter(input, output, delay=0.7, atten=4)
```
还有一种变化就是直接导入一个函数或者变量:
```python
from sound.effects.echo import echofilter
```
同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:
```python
echofilter(input, output, delay=0.7, atten=4)
```
注意当使用 `from package import item` 这种形式的时候,对应的 `item` 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
<u>设想一下,如果我们使用 `from sound.effects import *` 会发生什么?</u>
Python 会进入文件系统,找到这个包里面所有的子模块,一个一个的把它们都导入进来。
导入语句遵循如下规则:如果包定义文件 `__init__.py` 存在一个叫做 `__all__` 的列表变量,那么在使用 `from package import *` 的时候就把这个列表中的所有名字作为包内容导入。
这里有一个例子,在 `sounds/effects/__init__.py`中包含如下代码:
```python
__all__ = ["echo", "surround", "reverse"]
```
这表示当你使用 `from sound.effects import *`这种用法时,你只会导入包里面这三个子模块。
如果 `__all__` 真的没有定义,那么使用`from sound.effects import *`这种语法的时候,就不会导入包 `sound.effects` 里的任何子模块。他只是把包 `sound.effects` 和它里面定义的所有内容导入进来(可能运行`__init__.py`里定义的初始化代码)。
这会把 `__init__.py` 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。
```python
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
```
这个例子中,在执行 `from...import` 前,包 `sound.effects` 中的 `echo``surround` 模块都被导入到当前的命名空间中了。
通常我们并不主张使用 `*` 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。
---
<b>参考文献:</b>
- https://www.runoob.com/python3/python3-tutorial.html
- https://www.bilibili.com/video/av4050443
- https://blog.csdn.net/u010820857/article/details/85330778
---
**练习题**
1、怎么查出通过 from xx import xx导⼊的可以直接调⽤的⽅法?
2、了解Collection模块,编写程序以查询给定列表中最常见的元素。
题目说明:
输入:language = ['PHP', 'PHP', 'Python', 'PHP', 'Python', 'JS', 'Python', 'Python','PHP', 'Python']
输出:Python
```python
"""
Input file
language = ['PHP', 'PHP', 'Python', 'PHP', 'Python', 'JS', 'Python', 'Python','PHP', 'Python']
Output file
Python
"""
def most_element(language):
""" Return a list of lines after inserting a word in a specific line. """
# your code here
```