# 模块
您已经看到如何通过定义函数在程序中重用代码。如果你想在其它程序中重用一组函数,怎么办?你可能已经猜到了,答案就是模块。
编写模块有各种各样的方法,但是最简单的方法是创建一个以`.py`为扩展名,包含函数和变量的文件。
编写模块的另一种方式就像是编写Python解释器一样,可以使用[C 编程语言](http://docs.python.org/3/extending/index.html)编写模块。当它们被编译后,当使用标准的Python解释器时,你可以在Python代码中使用这些模块。
一个模块可以通过**imported(导入)**另一个程序而使用其功能。我们可以通过同样的方法使用Python标准库。首先 ,我们看一下如何使用标准库模块。
例子 (保存为module_using_sys.py):
```python
import sys
print('命令行参数是:')
for i in sys.argv:
print(i)
print('\n\nPYTHONPATH是', sys.path, '\n')
```
输出:
```
$ python module_using_sys.py we are arguments
命令行参数是:
module_using_sys.py
we
are
arguments
PYTHONPATH是 ['d:\\KanCloud\\from-python-to-django\\a-byte-of-python3', 'C:\\Users\\boris\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip',
# 还有很多,这里不一一列出
'C:\\Users\\boris\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages']
```
**它是如何工作的:**
首先,我们使用`import`语句**import(导入)**`sys`(系统)模块。通常情况下,这意味着我们告诉Python,我们想使用这个模块。`sys`模块包含了与Python解释器和其运行环境(如操作系统)有关的函数。
当Python执行`import sys`语句时,它会查找`sys`模块。在这里,它是一个内建模块,因此,Python知道到去哪里找到它。
如果它还没有被编译,也就是Python的源代码,那么,Python解释器将在`sys.path`目录列表中搜索。如果找到了这个模块,那么就执行模块中的代码,对你的程序来说,这个模块就变成**有效**的。注意,初始化只有在我们**第一次**导入一个模块时完成。
我们通过点符号访问`sys`模块中的`argv`变量,就像这样`sys.argv`。它表明,`argv`这个名字是`sys`模块的一部分。这种方法的另一个优点是,如果你的程序中使用了名为`argv`的变量,那么他们不会冲突。
`sys.argv`变量一个字符串**list(列表)**(我们会在[后面的章节](./data_structures.md)详细介绍)。具体来说,`sys.argv`包含**命令行参数**的列表,也就是使用命令行向你的程序传递的参数清单。
如果您正在使用IDE编写并运行这些程序,在菜单中寻找一种方法来指定命令行参数传递给你的程序。
这里,当我们执行`python module_using_sys.py we are arguments`时,我们使用 `python`命令和后面的参数运行`module_using_sys.py`模块。Python把命令行参数存储在`sys.argv`变量中供我们使用。
记住,运行脚本的名字通常是`sys.argv`列表中的第一个参数。因此,这里的`sys.argv[0]`是`'module_using_sys.py'`,`sys.argv[1]`是`'we'`,`sys.argv[2]`是`'are'`和`sys.argv[3]`是`'arguments'`。注意,Python从0而不是1开始计数。
`sys.path`包含可以被导入的模块所在的目录名列表。我们注意到`sys.path`的第一个字符串就是程序的当前路径。这意味着你可以直接导入位于当前目录中的模块。否则,你必须把你的模块放在`sys.path`列表中的一个目录中。
请注意,当前目录是程序启动的目录。运行`import os; print(os.getcwd())`找到你的程序的当前目录。
## 字节编译的.pyc文件
导入整个模块是一个代价相对较高的事情,所以Python提供了一些技巧使它可以效率更高。一种方法是创建扩展名为`.pyc`的**字节编译**文件,这是Python将程序转换成的一种中间形式(我们在[简介](introduction.md)中介绍过Python是如何工作的。当你下次从一个不同程序导入模块时,这种`.pyc`文件是有很用的--它将快得多,因为导入模块一部分需要处理的工作已经完成了。同时,这些字节编译的文件是与操作系统平台无关的。
注意:这些`.pyc`文件通常在与之相应的`.py`文件的同一个目录中创建。如果Python在那个目录中没有写入权限,那么就**无法**创建`.pyc`文件。
## from ... import语句
如果你想直接导入`argv`变量到程序中(为了避免每次为它键入`sys.`),可以使用`from sys import argv`语句。
> 一般来说,应该**避免**使用这个语句,尽量使用`import`语句,这样可以在程序中避免名称冲突,使你的程序更具有可读性。
例如:
```python
from math import sqrt
print("16的平方根是", sqrt(16))
```
## 模块的`__name__`
每个模块都有一个名字,在模块内部的语句可以获得它所在的模块的名字。当我们想搞清楚模块是直接运行还是被导入的时候,这种设计很方便。前面提到过,当一个模块被第一次导入时,其所包含的代码被执行。我们可以通过使用这个特性,根据模块是直接运行还是从另一个模块被导入,执行不同的操作。我们可以通过使用模块的`__name__`属性来实现上述功能。
例子 (保存为 module_using_name.py):
```python
if __name__ == '__main__':
print('这个程序正在被自己运行')
else:
print('我从别的模块被导入')
```
输出:
```shell
C:\> python module_using_name.py
这个程序是直接运行的
C:\> python3
>>> import using_name
我从被别的模块导入
>>>
```
**它是如何工作的:**
每个Python模块都会定义自己的`__name__`,如果是`'__main__'`,这意味着模块在被用户直接运行,我们可以执行相应的操作。
## 制作属于你自己的模块
创建自己的模块是很容易的,就像你一直在这样的那样。因为每个Python程序就是一个模块。你只需要确保它有一个`.py`扩展名。下面的例子会让你明白这一点。
例子 (保存为mymodule.py):
```python
def say_hi():
print('嗨,这就是我的模块。')
__version__ = '0.1'
```
上面的是**模块**的一个例子。正如您看到的,和我们通常的Python程序相比,没有什么特别的。接下来我们看一下如何在我们其它的程序中使用这个模块。
记住,该模块要么放置在我们导入它的程序同一个目录中,要么放置在`sys.path`目录列表中的一个目录中。
另一个模块(保存为mymodule_demo.py):
```python
import mymodule
mymodule.say_hi()
print('版本', mymodule.__version__)
```
输出:
```shell
C:\> python mymodule_demo.py
嗨,这就是我的模块。
版本 0.1
```
**它是如何工作的:**
注意,我们使用相同的点符号来访问模块的成员。Python充分重用相同的符号产生了独特的'Pythonic'的感觉,这样我们不需要总是学习新的方法来做事情。
这是使用`from..import`语法的一个版本(保存为mymodule_demo2.py):
```python
from mymodule import say_hi, __version__
say_hi()
print('版本', __version__)
```
`mymodule_demo2.py`和`mymodule_demo.py`的输出相同。
注意,如果在导入模块中已经有一个`__version__`名字的声明,这里会有一个冲突。这也可能是因为它是常见的做法--对于每个模块使用这个名字声明它的版本号。因此,我们推荐使用`import`语句,虽然它可能让你的程序有点长。
你还可以使用:
```python
from mymodule import *
```
这将导入所有的公共名称如 say_hi,但不会导入__version__,因为它始于双下划线。
> 注意:尽量不要使用`import *`这种方式,诸如`from mymodule import *`等
<!-- -->
> **Python之禅**
>
> Python的一个指导原则是"显式优于隐式"。运行`import this`去学习更多相关的信息。
## `dir`函数
您可以使用内置的`dir()`函数列出一个对象定义的标识符。例如,对于一个模块,包括在模块中已经定义的函数、类和变量。
`dir()`函数有参数。当你给`dir()`提供一个模块名字时,它返回在那个模块中定义的名字的列表。当没有为其提供参数时, 它返回当前模块中定义的名字的列表。
例如:
```shell
C:\>python
>>> import sys
# 获得属性列表,在这里是sys模块的属性列表
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_base_executable', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_enablelegacywindowsfsencoding', '_framework', '_getframe', '_git', '_home', '_xoptions', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'set_coroutine_wrapper', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions', 'winver']
# 获得当前模块的属性列表
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
# 创建了一个新变量 'a'
>>> a = 5
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'sys']
# 删除/移除一个名字
>>> del a
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'sys']
>>>
```
**它是如何工作的:**
首先,我们在导入的`sys`模块上使用`dir`。我们能看到模块包含的巨大的属性列表。
然后,我们使用没有传递参数的`dir`函数。默认情况下,它返回当前模块的属性列表。注意,导入的模块仍然是这个列表的一部分。
为了看到`dir`在起作用,我们定义了一个新的变量`a`,并为其赋值,然后检查`dir`,我们发现列表中添加了一个同名变量。我们使用`del`语句移除当前模块的变量或属性,在`dir`函数的输出中变化再次得到体现。
关于`del`的一点注意事项--这个语句用于**删除**一个变量/属性,语句运行后(这里是`del a`),你不能再访问变量`a`--就像它从来根本没有存在过。
注意,`dir()`函数对**任何**对象都起作用。例如,运行`dir('str')`来学习`str`(string)类型的更多知识。
还有一个[`vars()`](http://docs.python.org/3/library/functions.html#vars)函数可以列出所有的属性及其值,但并不是在所有情况下都起作用。
## 包(Packages)
现在,你必须开始观察组织你的程序的层次结构。变量通常在函数内部。函数和全局变量通常在模块内部。如果你想组织模块?这就涉及到打包了。
包只是模块的文件夹,使用一个特殊的`__init__.py`文件,告诉Python这个文件夹是特殊的,因为它包含Python模块。
假设你想创建一个叫做'世界'的程序包,分装'亚洲'、'非洲'等等,分包按序包含'印度'、'马达加斯加'等等。
这是你的文件结构:
```
- <在sys.path中现有的一些文件夹>/
- world/
- __init__.py
- asia/
- __init__.py
- india/
- __init__.py
- foo.py
- africa/
- __init__.py
- madagascar/
- __init__.py
- bar.py
```
包只是为了方便按照层次组织模块。在[Python标准库](./stdlib.md)中,你会看到包的许多实例。
## 小结
就像函数是可重用的一部分程序一样,模块也是可重用的程序。包是组织模块的更高一级的层次结构。Python自带的标准库是一系列包和模块的例子。
我们已经看到了如何使用这些模块和创建我们自己的模块。
接下来,我们将学习一些有趣的称为数据结构的概念。
- 开始学习
- 搭建Python开发环境
- 简明Python教程
- 致敬
- 前言
- 关于Python
- 安装
- 第一步
- 基础
- 运算符和表达式
- 控制流
- 函数
- 模块
- 数据结构
- 实战案例
- 面向对象编程
- 输入与输出
- 异常处理
- 标准库
- 更多
- 继续学习
- 附录:免费/自由和开放源码软件
- 附录: 关于
- 附录: 版本历史
- 附录: 翻译
- 附录: 参与翻译工作
- 反馈
- Django Step Sy Step
- 第一讲 从简单到复杂
- 第二讲 做加法的例子
- 第三讲 使用Template
- 第四讲 生成csv格式文件
- 第五讲 session示例
- 第六讲 wiki的例子
- 第七讲 通讯录的例子
- 第八讲 文件导入和导出
- 第九讲 通讯录的美化
- 第十讲 扩展django的模板
- 第十一讲 用户管理
- 第十二讲 搜索和部署
- 第十三讲 Ajax的实现(一)
- 第十四讲 Ajax的实现(二)
- 第十五讲 i18n的一个简单实现
- 第十六讲 自定义Calendar Tag
- 第十七讲 View,Template和Tag
- Django开发实战
- Python开发规范
- Django项目的gitignore
- 怎样配置开发环境的settings
- 如何使用Django和Vue.js构建项目
- 使用WebSocket开发网页聊天室
- 怎样使Django Admin显示中文
- 怎样使Model在Admin界面中显示中文
- 使用Django Admin怎样上传并显示图片
- 解决Django模板和Vue指令花括号冲突的问题
- 使用Django和Vue开发微信公众号
- 使用Django和Vue调用微信JSSDK开发微信支付