模块:
python 中的模块相当于 C/C++ 中的头文件,在 C/C++ 中需要通过 #include 导入头文件,才可以使用头文件中的函数,而在 python 中导入模块,要使用 import 关键字,导入模块之后,才可以使用模块中的方法;
python 中的模块是以 .py 为后缀的文件,它既可以包含函数,也可以包含变量;模块文件也是源码文件;
python 中的所有模块,可以在安装路径下的 lib 文件夹下查看,如下所示:
ubuntu 环境下 python3 模块的位置在 /user/lib/python3.5/:
pip:
python 在安装好之后,就会自带很多模块,例如 sys、os、random 等;
但是除了 python 自带的模块之外,我们通常还需要使用一些第三方的模块,这时候就需要我们自己安装了;
而 pip 就是 python 里的模块管理工具,可以用来安装第三方的模块;
在 windows 系统下安装 python 的时候,会自动安装 pip 工具,可以使用 pip --version 命令查看:
然后使用 pip 安装 pygame 模块,直接在终端输入命令:pip install pygame 即可,如下:
查看安装好的模块的详细信息:
但是在导入 pygame 模块的时候,总是会输出 pygame 模块的版本信息:
解决办法是到 pygame 安装的路径下,找到 __init__.py 文件:
然后打开该文件,找到下面的输出语句,注释掉即可:(我的这条输出语句是倒数第二个语句块)
而在 ubuntu 下安装模块时需要注意,因为 pip 工具是在 python 2.x 下的,所以用 pip 工具安装模块时也是将模块安装到 python 2.x 中的;查看 pip 版本的时候可以看到如下信息:
如果 ubuntu 系统下默认没有安装 pip 工具,可以用 sudo apt install python-pip 命令安装:
如果你用的是 python 3.x,那么也可以不用安装 pip 工具,直接安装 pip3 工具即可,
查看安装好之后的 pip3 工具:
安装好 pip3 工具之后,直接用 sudo pip3 install pygame 命令,就可以将 pygame 模块安装到 python 3.x 下了;
但是我用 pip3 工具安装模块时,报如下错误:
Exception:
Traceback (most recent call last):
File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/response.py", line 226, in _error_catcher
yield
File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/response.py", line 301, in read
data = self._fp.read(amt)
......
File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/response.py", line 231, in _error_catcher
raise ReadTimeoutError(self._pool, None, 'Read timed out.')
requests.packages.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.
这是因为用国内网络安装太慢,导致的超时错误,可以使用一些国内的镜像:
清华大学: https://pypi.tuna.tsinghua.edu.cn/simple
阿里云: http://mirrors.aliyun.com/pypi/simple
中国科技大学: https://pypi.mirrors.ustc.edu.cn/simple
豆瓣(douban): http://pypi.douban.com/simple
中国科学技术大学: http://pypi.mirrors.ustc.edu.cn/simple
临时使用镜像可以在 pip3 工具后面加参数 -i,指定源,如下所示:
sudo pip3 install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple
永久使用镜像:修改 ~/.pip/pip.conf (没有就创建一个), 内容如下:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
但是,我用这种配置文件的方式,还是会报超时异常;
参考资料:http://www.mamicode.com/info-detail-2349450.html
pip 模块管理工具还有一些其他的用法:
- 安装已经下载好的安装包:
pip install 下载好的安装包
- 查看已经安装了哪些模块:
pip list
- 显示已安装模块的具体信息:
pip show --files pygame
- 列出哪些软件包已过期,即有新的软件包可以升级:
pip list --outdated
- 升级软件包:
windows 系统下要在 pip 工具前面加 python -m,否则会升级失败:
python -m pip install --upgrade pip
- 卸载软件包:
pip uninstall pygame
自定义模块:
一个模块就是一个 .py 文件,里面可以包含函数和变量;
比如定义一个模块叫 MyMath,就是创建一个 MyMath.py 文件;然后在文件中声明一个变量,和一个函数:
# 变量
dec = "自定义的模块"
# 函数:求和
def getSum(a, b):
return a + b
然后在主程序中导入该模块,并使用模块中的函数和变量:
# 导入自定义的模块:import 模块文件的名字(不用带 .py 后缀)
import MyMath
print("MyMath.dec:", MyMath.dec) # 使用模块中的变量
print("2 + 3 = ", MyMath.getSum(2, 3)) # 使用模块中的函数
输出结果:
注意:在使用我们自定义的模块时,会自动生成一个 __pycache__ 文件夹,然后在该文件夹中自动生成一个 MyMath.cpython-38.pyc 文件;
这个文件是 python 解析器将 MyMath 模块解析之后生成的字节码文件,相当于 java 的 .class 文件;cpython 表示 python 解析器是用 c 语言写的,38 表示 python 的版本是 3.8,.pyc 是字节码文件的后缀名;
导入模块:
在 python 中可以用 import 或 from ... import ... 来导入模块;
- 将整个模块(somemodule)导入,格式为:import somemodule
# 导入 MyMath 模块
import MyMath
# 使用 MyMath 模块中的变量和函数:
# 如果只导入了模块名,那么在使用模块中的变量和函数时,必须在前面加上模块名
print("MyMath.dec:", MyMath.dec) # 使用模块中的变量
print("MyMath.getSum:", MyMath.getSum(2, 3)) # 使用模块中的函数
print("getSum:", getSum(2, 3)) # 如果加模块名会报错:name 'getSum' is not defined
- 将某个模块中的某个函数(somefunction)导入,格式为:from somemodule import somefunction
# 将 MyMath 模块中的 getSum 函数导入
from MyMath import getSum
# 因为已经导入了 getSum 函数,所以可以直接使用
print("getSum:", getSum(2, 3))
# 反而如果在 getSum 函数前面加模块名,就会报错:name 'MyMath' is not defined
# print("MyMath.getSum:", MyMath.getSum(2, 3))
# 因为导入的是 MyMath 模块中的 getSum 函数,而不是导入了 MyMath 模块,
# 所以不能使用 MyMath 模块中的其他功能;
print("MyMath.dec:", MyMath.dec) # 报错:name 'MyMath' is not defined
- 将某个模块中的多个函数或变量导入,格式为:from somemodule import firstfunc, secondfunc
# 将 MyMath 模块中的 dec 变量和 getSum 函数导入
from MyMath import dec, getSum
# 因为已经导入了 dec 变量和 getSum 函数,所以可以直接使用
print("dec:", dec)
print("getSum:", getSum(2, 3))
- 将某个模块中的全部函数和变量导入,格式为:from somemodule import *
# 将 MyMath 模块中的全部变量和函数导入
from MyMath import *
# 因为已经导入了 MyMath 模块中的所有功能,所以可以直接使用
print("dec:", dec)
print("getSum:", getSum(2, 3))
- 为导入的模块起别名:(如果模块的名称比较长,可以使用别名)
# 导入 MyMath 模块,并为 MyMath 模块起个别名 mm
import MyMath as mm
# 使用 mm 代替 MyMath 调用模块中的变量和函数
print("dec:", mm.dec)
print("getSum:", mm.getSum(2, 3))
注意:如果在不同的模块中,有名称相同的函数都被导入了,那么后一次导入的函数有效;所以,应尽量避免使用 import * 这种方式,因为存在一定的风险;推荐使用 import somemodule 的方式,可以明确的知道哪个函数出自那个模块;
导入模块的时候,先在当前项目路径下进行搜索,如果搜索不到,再到 python 系统目录下进行搜索;
模块中的私有属性:
python 中变量命名的规则:
- num:前后都没有下划线的是公有变量;
- __num:前置双下划线的是私有变量;
- __init__:前后双下划线的是 python 系统内置的变量;
- _num:前置单下划线的是私有化的属性;私有化的属性在类中的用法跟公有属性一样,类对象和子类都可以访问;但是如果私有属性用在模块中,当使用 from somemodule import * 导入模块时,私有属性无法使用;
- num_:单后置下划线用于避免与 python 关键词的冲突;比如 if_ 可以作为一个变量;
将自定义模块 MyMath.py 改为:
# 变量
dec = "自定义的模块"
# 模块中的私有属性
_num = 100
__num2 = 200
# 函数:求和
def getSum(a, b):
return a + b
然后在外部使用 from MyMath import * 方式导入模块:
# 将 MyMath 模块中的全部变量和函数导入
from MyMath import *
# 因为已经导入了 MyMath 模块中的所有功能,所以可以直接使用
print("dec:", dec)
print("getSum:", getSum(2, 3))
# 无法使用模块中的私有属性
print("_num:", _num)
print("__num2:", __num)
但是如果在外部使用 import MyMath 方式导入模块,则可以使用模块中的私有属性:
# 导入 MyMath 模块
import MyMath
# 可以使用模块中的私有属性
print("_num:", MyMath._num)
print("__num2:", MyMath.__num2)
在模块中进行测试(__name__ 变量的用法):
python 中在导入一个模块的时候,会将模块中的代码从头到尾执行一遍;如果我们在模块中写了测试程序输出一些信息,那么在主程序中导入该模块的时候,也会执行模块中的测试代码输出信息;
例如,在模块中写一个测试语句,如下所示:
# 变量
dec = "自定义的模块"
# 函数:求和
def getSum(a, b):
return a + b
print("测试:", getSum(22, 33))
然后在主程序中导入该模块:
# 导入 MyMath 模块
import MyMath
# 使用模块中的函数
print("getSum:", MyMath.getSum(2, 3))
输出结果:
那么应该怎么解决在主程序中导入模块时,不执行模块中的测试代码呢?
此时需要用到一个系统内置的变量 __name__,我们在 MyMath 模块中输出该变量的值:
# 变量
dec = "自定义的模块"
# 函数:求和
def getSum(a, b):
return a + b
# 输出 __name__ 变量的值
print("__name__:", __name__)
输出该变量的值为:__main__
但是,当我们在外部程序中导入 MyMath 模块,因为导入模块的时候会执行一遍模块中的所有代码,此时模块中输出 __name__ 变量的值变成了 MyMath 模块的名字,如下:
所以,如果我们想在模块中写一些测试代码,但是又不想被外部程序导入该模块的时候,执行这些测试代码,可以如下操作:
# 变量
dec = "自定义的模块"
# 函数:求和
def getSum(a, b):
return a + b
# 测试代码都写在该条件语句中,在外部导入该模块的时候,就不会执行了
if __name__ == "__main__":
print("测试:", getSum(22, 33))
模块中 __all__ 变量的用法:
我们知道导入模块中的所有功能可以使用方法:from somemodule import *,而 __all__ 变量的作用就是在使用该方法时限制导入模块中的某些功能;
自定义一个模块,名字为 sendMsg.py,在模块中使用 __all__ 变量:
# __all__ 变量的作用是限制外部使用 from sendMsg import *
# 方法导入该模块时,只能导入 __all__ 变量指定的成员;
# __all__ 变量的值是一个字符串列表,列表中的元素不管是变量,
# 还是函数,或是类,都用字符串表示;
# 下面的语句表示 from sendMsg import * 导入该模块时,
# 只能使用模块中的变量 num 和 test1 函数,而不能使用 test2 函数;
__all__ = ["num", "test1"]
# 在模块中声明一个全局变量
num = 100
# 在模块中定义两个函数
def test1():
print("=== test1 ===")
def test2():
print("=== test2 ===")
在 main.py 中导入 sendMsg 模块:
# 导入模块
from sendMsg import *
# 调用模块中的变量
print("num:", num)
# 调用模块中的方法
test1()
test2() # 会调用失败
输出结果:
包:
一个模块是指一个 .py 文件,而包是一个包含多个 .py 文件的文件夹,并且该文件夹中必须有一个 __init__.py 文件,有了该文件才能被称之为包;即如果文件夹中含有多个 .py 文件,但是没有 __init__.py 文件,则只是一个普通的文件夹,不能称之为包;如下所示;
其中 recvMsg.py 中有一个函数:
# 在 recvMsg 模块中定义一个函数
def recvTest():
print("=== recvTest() ===")
而 sendMsg.py 中也有一个函数:
# 在 sendMsg 模块中定义一个函数
def sendTest():
print("=== sendTest() ===")
当然,一个普通的文件夹下包含多个模块,也是可以通过下面方法被外部导入的:
但是,如果文件夹下有很多模块,我们就需要写很多个 from ... import ... 语句,那么能不能通过 import msg 或者 from msg import * 来导入 msg 文件夹下的所有模块呢?答案是否定的。但是如果 msg 是包,就可以实现。
不过,如果 __init__.py 为空,也不能直接导入包中的模块,需要对 __init__.py 进行编辑;
1、在 __init__.py 中使用 __all__ 变量:
前面已经讲过 __all__ 变量用在模块中,用于指定外部使用 from 模块名 import * 导入该模块时,可以使用模块中的哪些功能;
而包中使用 __all__ 变量,用于限制外部使用 from 包名 import * 导入该包时,可以使用包中的哪些模块;
比如在 __init__.py 中添加如下代码:
__all__ = ["sendMsg"]
表示外部只能使用包中的 sendMsg 模块:
# 导入包中的模块
from msg import *
# 使用包中的模块
# 因为 __init__ 中指定 __all__ 变量的值为 sendMsg,
# 所以可以使用 sendMsg 模块中的所有功能,
# 而不能使用 recvMsg 模块中的功能;
sendMsg.sendTest()
recvMsg.recvTest()
输出结果:
但是,__all__ 变量只能影响 from msg import * 方法,当外部使用 import msg 的时候,还是不能使用包中的模块;
2、在 __init__.py 中导入包中的模块:
用于指定当外部使用 import msg 导入包的时候,可以使用包中的哪些模块;修改 __init__.py 文件为:
# 导入包中的模块,下面两种方法都可以
from . import sendMsg # 从当前路径下导入模块
from msg import recvMsg # 从 msg 文件夹下导入模块
在 main.py 中导入 msg 包时,就可以直接使用包中的模块了:
# 导入包
import msg
# 使用包中的模块
msg.sendMsg.sendTest()
msg.recvMsg.recvTest()
3、在 __init__.py 中输出数据:
__all__ = ["sendMsg"]
# 在 __init__.py 中输出数据
print("=== init ===")
当外部导入包时,会执行 __init__.py 中的所有代码,所以会直接输出数据;
前面我们已经讲过,当导入模块时,也会执行模块中的所有代码;
包的发布(安装):
我们知道,当导入一个模块的时候,首先会在当前项目路径下搜索该模块,如果搜不到,再到 Python 系统路径下搜索;导入包也是一样的;
而我们自己创建的模块或包,只能在当前项目路径下才能使用,如果换个其他的项目,或者路径,就不能使用了;
如果想不管在什么路径下都可以使用我们自定义的模块或包,那么就需要把包发布到 python 的系统路径下;
发布包的步骤:
1、在包的同级目录下创建一个 setup.py 文件,里面的内容如下:
# 这是固定写法
from distutils.core import setup
# name:生成的包的名字
# version:包的版本
# description:包的描述信息
# author:作者
# py_modules:用来指定包中的模块
setup(name="chenhx", version="1.0", description="chenhx's module", author="chenhx", py_modules=['msg.sendMsg', 'msg.recvMsg'])
2、执行 python setup.py build 命令构建包:
3、执行 python setup.py sdist 命令生成发布压缩包:
4、安装:
将 chenhx-1.0.tar.gz 随便放在哪个路径下都可以,然后解压 chenhx-1.0.tar.gz,解压之后变成:
然后在命令提示符下进入该路径,执行 python setup.py install 命令进行安装:
5、到此为止,已经将包发布到了 python 的系统路径下了,不管在什么位置都可以使用了;我们在 D 盘根目录下创建一个 main.py 文件,内容为:
# 导入包
import msg
# 使用包中的模块
msg.sendMsg.sendTest()
msg.recvMsg.recvTest()
执行结果为: