目录
10.1 构建一个模块的层级包
怎么把代码组织成由很多分层模块构成的包?
封装成包是很简单的。在文件系统上组织你的代码,并确保每个目录都定义了一个__init__.py文件。
__init__.py
的作用是让一个呈结构化分布(以文件夹形式组织)的代码文件夹变成可以被导入import
的软件包。
示例包:
test/
__init__.py
A/
__init__.py
A1.py
B/
B1.py
I/
import.py
在 import.py对 A.A1 和 B.B1调用:
import sys
sys.path.append(r'C:\Users\Administrator\Desktop\test')
import A.A1
import B.B1
即使没有__init__.py文件存在,python仍然会导入包。如果你没有定义__init__.py时,实际上创建了一个所谓的“命名空间包”
10.2 控制模块被全部导入的内容
当使用’from module import *’ 语句模块导入变量名(函数名、类名)时,怎么控制不导入全部的变量名?
可以在模块中定义一个变量 __all__ 来明确地列出需要导出的内容
例如:__all__ = ['spam', 'grok'] ,那么只会导入 spam , grok 两个变量名。
如果 __all__列表中有为定义的变量名,导入会引起 AttributeError
10.3 使用相对路径名导入包中子模块
10.4 将模块分割成多个文件
示例:一个模块中有两个类,把这个模块分为两个文件,每个文件定义一个类,怎么做?
用模块同名目录来替换该模块,新建两个文件,类代码分别插入。
from module import A,B
10.5 利用命名空间导入目录分散的代码
删去用来将组件联合起来的__init__.py文件
包命名空间是一种特殊的封装设计,为合并不同的目录的代码到一个共同的命名空间。对于大的框架,这可能是有用的,因为它允许一个框架的部分被单独地安装下载。它也使人们能够轻松地为这样的框架编写第三方附加组件和其他扩展。
包命名空间的关键是确保顶级目录中没有__init__.py文件来作为共同的命名空间。
10.6 重新加载模块
因为对已经加载的模块源码进行了修改,所以需要重新加载该模块,怎么做?
使用 importlib.reload() 来重新加载先前加载的模块(imp已废弃)
>>> import re
>>> import importlib
>>> importlib.reload(re)
<module 're' from 'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\lib\\re.py'>
重新加载模块在开发和调试过程中常常很有用。但在生产环境中可能需要避免重新加载模块,因为它并不总是像您期望的那样工作,不安全。
reload()擦除了模块底层字典的内容,并通过重新执行模块的源代码来刷新它。模块对象本身的身份保持不变。因此,该操作在程序中所有已经被导入了的地方更新了模块。
10.7 运行目录或压缩文件
如果应用程序由多个文件组成,怎么运行这个应用?
在应用程序的目录中添加一个__main__.py文件,如果__main__.py存在,可以在顶级目录运行 Python解释器:
#示例应用
OneApplication/
A.py
B.py
C.py
__main__.py
#在命令提示符运行
> python OneApplication
如果将代码打包成zip文件,这种技术同样也适用
10.8 读取位于包中的数据文件
怎么读取位于包中的数据文件?
要读取数据文件可以使用内置的I/ O功能,如open()。但是这种方法有一些问题。
首先,一个包对解释器的当前工作目录几乎没有控制权,因此编程时任何I/O操作都必须使用绝对文件名。使用模块包的完整路径的__file__变量也很凌乱。另外,包通常作为.zip或.egg文件安装,这些文件并不像在文件系统上的一个普通目录里那样被保存,因此用open()对一个包含数据文件的归档文件进行操作,它根本不会工作。
pkgutil.
get_data
(package,resource )
pkgutil.get_data() 函数是一个读取数据文件的高级工具,不用管包是如何安装以及安装在哪。它只是工作并将文件内容以字节字符串返回给你
get_data()的第一个参数是包含包名的字符串。你可以直接使用包名,也可以使用特殊的变量,比如__package__。第二个参数是包内文件的相对名称。如果有必要,可以使用标准的Unix命名规范到不同的目录,只要最后的目录仍然位于包中。
示例:
#示例包,somedata.dat 是要读取的数据文件
OnePackage/
__init__.py
somedata.dat
A.py
#在A中读取
import pkgutil
data = pkgutil.get_data(__package__, 'somedata.dat')
# 返回字节字符串格式
10.9 将文件夹加入到sys.path
有两种常用的方式将新目录添加到sys.path。
第一种,你可以使用PYTHONPATH环境变量来添加,命令窗口添加路径:
export PYTHONPATH=$PYTHONPATH:/home/ershisui
此方法只在当前命令窗口生效,在自定义应用程序中,这样的环境变量可在程序启动时设置或通过shell脚本。
第二种方法是创建一个.pth文件,将目录列举出来,示例:
# OneApplication.pth
/some/dir
/other/dir
这个.pth文件需要放在某个Python的site-packages目录,通常位于/usr/local/lib/python3.3/site-packages 或者 ~/.local/lib/python3.3/sitepackages。当解释器启动时,.pth文件里列举出来的存在于文件系统的目录将被添加到sys.path。安装一个.pth文件可能需要管理员权限,如果它被添加到系统级的Python解释器。
或者 使用sys.path.append() 方法临时添加搜索路径,方便更简洁的import其他包和模块。这种方法导入的路径会在python程序退出后失效。
import sys
sys.path.insert(0, '/some/dir')
# sys.path.insert()可以设置搜索的优先级,序号从0开始
但这样容易导致维护问题,可改为:
import sys
from os.path import abspath, join, dirname
sys.path.insert(0, join(abspath(dirname(__file__)), 'src'))
os.path.abspath(path) | 返回绝对路径 |
os.path.join(path1[, path2[, ...]]) | 把目录和文件名合成一个路径 |
os.path.dirname(path) | 返回文件路径 |
10.10 通过字符串名导入模块
要导入的模块或包是字符串格式,怎么对字符串调用导入命令?
使用importlib.import_module()函数来手动导入名字为字符串的模块或者包。import_module执行和import相同的步骤,返回生成的模块对象,将其存储在一个变量,对变量像正常的模块一样使用。
>>> import importlib
#模块
>>> m = importlib.import_module('math')
>>> m.sin(0.5)
0.479425538604203
#包
>>> b = importlib.import_module('.b', __package__)
#相当于 from . import b
手动导入模块通常在以某种方式编写修改或覆盖模块的代码时候使用。例如,也许你正在执行某种自定义导入机制,需要通过名称来加载一个模块,通过补丁加载代码。
10.11 通过钩子远程加载模块
自定义Python的import语句,使得它能从远程机器上面透明的加载模块。
10.12 导入模块的同时修改模块
本节技术依赖于10.11小节中讲述过的导入钩子,并稍作修改。
https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p12_patching_modules_on_import.html
10.13 安装私有的包
要安装一个供自己使用的包,而不是系统上面所有用户,或者要安装一个第三方包,但是没有权限将它安装到系统Python库中去,怎么做?
Python有一个用户安装目录,通常类似”~/.local/lib/python3.3/site-packages”。 要强制在这个目录中安装包,可使用安装选项“–user”:
pip install --user packagename
在sys.path中用户的“site-packages”目录位于系统的“site-packages”目录之前。 因此,你安装在里面的包就比系统已安装的包优先级高 (尽管并不总是这样,要取决于第三方包管理器,比如distribute或pip)。
通常包会被安装到系统的site-packages目录中去,路径类似“/usr/local/lib/python3.3/site-packages”。 不过,这样做需要有管理员权限并且使用sudo命令。 就算你有这样的权限去执行命令,使用sudo去安装一个新的,可能没有被验证过的包有时候也不安全。
安装包到用户目录中通常是一个有效的方案,它允许你创建一个自定义安装。
另外,你还可以创建一个虚拟环境,这个我们在下一节会讲到。
10.14 创建虚拟环境 (待补 从Django项目)
https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p14_creating_new_python_environment.html
10.15 分发包
第一件事就是给它一个唯一的名字,并且清理它的目录结构
编写一个 setup.py
下一步,就是创建一个 MANIFEST.in
文件,列出所有在你的包中需要包含进来的非源码文件
确保 setup.py
和 MANIFEST.in
文件放在你的包的最顶级目录中。 一旦你已经做了这些,你就可以像下面这样执行命令来创建一个源码分发包了:
% bash python3 setup.py sdist
它会创建一个文件比如”projectname-1.0.zip” 或 “projectname-1.0.tar.gz”, 具体依赖于你的系统平台。如果一切正常, 这个文件就可以发送给别人使用或者上传至 Python Package Index.
https://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p15_distributing_packages.html