11. 包和模块的奇淫巧技

  1. 构建简单的包

在项目代码中,如果需要形成分层模块构成的包

只需要在文件系统上分别定义不同的文件夹,并且每个目录都定义了 __init__.py文件

graphics/

__init__.py

primitive/

__init__.py

line.py

fill.py

text.py

formats/

__init__.py

png.py

jpg.py

这样就可以进行import了

__init__.py会在导入的时候被导入,建立这个目录,也就是命名空间

如果我们递归的进入到更深处,那么每一层的init.py都会被导入

一般来说我们不会在init.py中书写代码,特殊情况需要用其加载子模块

甚至有时候我们都不会定义init.py,让其成为一个命名空间包即可

  1. 控制可以被导入的内容

一般来说我们想要某个类或者方法不被导入,会在起名的时候在前面加上两道下划线

但如果我们想要控制 import.*的动作,则应该需要定义一个变量 __all__来进行控制,只有包含在all变量数组中的东西,才会被导入

__all__ = [‘spam’, ‘grok’]

  1. 相对路径导入

如果同在一个项目中,那么是可以使用相对路径导包的

比如我们有如下路径的项目

mypackage/

__init__.py

A/

__init__.py

spam.py

grok.py

B/

__init__.py

bar.py

如果我们在spam.py下要导入grok

那么可以是from . import grok

如果要导入 bar,那么可以使用 from ..B import bar

这样使用比绝对路径来说,是一种更轻量的软变化

但是需要注意,只能在包的目录内定义

  1. 不设置init的目录

如果一个目录不设置init的话,那么我们可以联合起来,作为一个命名空间使用

就比如下面的代码

foo-package/

spam/

blah.py

bar-package/

spam/

grok.py

这样我们有一个共同的spam空间

我们如果将上面的foo-package和bar-package联合导入的话

>>> sys.path.extend([‘foo-package’, ‘bar-package’])

>>> import spam.blah

>>> import spam.grok

那么我们可以直接导入spam下的blah和grok的

这样我们可以看下spam这个包的命名空间

>>> import spam

>>> spam.__path__

_NamespacePath([‘foo-package/spam’, ‘bar-package/spam’])

  1. 重新加载模块

我们可以使用imp.load来重新加载模块

>>> imp.reload(spam)

重新加载模块一般很有用,会擦除底层字典的内容来进行刷新,但是有些地方不会刷新,比如 from xxxx import xxx

这种就不会刷新

所以应该避免重新加载模块

  1. 运行压缩文件

如果我们希望运行一个项目,那么我们应该将自己的目录放在一个 __main__,py文件中

myapplication/

spam.py

bar.py

grok.py

__main__.py

然后直接运行即可

bash % python3 myapplication

如果我们将项目打包为zip文件,那么也可以直接使用

bash % python3 myapplication.zip

  1. 添加文件夹到sys.path

如果想要添加新的模块到sys.path下,那么我们有两种方式

一种方式如下

bash % env PYTHONPATH=/some/dir:/other/dir python3

或者是创建一个.pth文件,进行列举

# myapplication.pth

/some/dir

/other/dir

.pth文件放在python的site-packages目录

在运行的时候也可以通过sys.path.insert 添加

sys.path.insert(0, ‘/some/dir’)

这种方式非常不推荐,因为往往换环境就无法使用了,所以很不建议

  1. 通过字符串来导入包

我们可以使用importlib.import_module() 来进行手动导入

>>> math = importlib.import_module(‘math’)

>>> math.sin(2)

  1. 安装第三方包

一般来说直接 pip install即可,但是可能存在着用户权限不足的原因

那么我们可以使用 -user来为某一个用户进行安装

pip install –user packagename

在sys.path中存在着用户的site-packages目录,其优先级由于site-packages目录

在里面安装的包比系统已经安装的包优先级高

  1. 创建虚拟环境

如果需要创建一个新的python环境,这个新环境安装的包和模块不会影响到系统级别的python环境

这时候可以使用pyenv来创建一个虚拟环境,会创建一个新的虚拟环境

bash % pyvenv Spam

这时候会创建一个Spam目录

进入到其中会存在新的编译器

bash % Spam/bin/python3

Python 3.3.0 (default, Oct 6 2012, 15:45:22)

[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin

这时候再进行安装的话,因为site-package是一个新的目录,所以不会影响系统环境

并且我们还是用的虚拟环境的解释器,双重保障

  1. 分发包

如果希望将自己的库分享出去

比如我们有一个如下的库

projectname/

README.txt

Doc/

documentation.txt

projectname/

__init__.py

foo.py

bar.py

utils/

__init__.py

spam.py

grok.py

examples/

helloworld.py

如果希望分享的话,那么我们可以编写一个setup.py

from distutils.core import setup

setup(name=’projectname’,

version=’1.0′,

author=’Your Name’,

author_email=’you@youraddress.com’,

url=’http://www.you.com/projectname’,

packages=[‘projectname’, ‘projectname.utils’],

)

以及一个MANIFEST.in文件,列出所有需要包含的非源码文件

# MANIFEST.in

include *.txt

recursive-include examples *

recursive-include Doc *

然后将这两个文件放在包的最顶级,这样就可以进行源码分发包了

% bash python3 setup.py sdist

这样就会创建一个”projectname-1.0.zip” 或 “projectname-1.0.tar.gz”

需要注意的是,setup.py中需要列出所有包源码的子目录,一个常见的错误就是列出了所有包的顶级目录,忘了其包含的子组件

这里需要我们可以考虑使用setuptools,distribute等进行代替

发表评论

邮箱地址不会被公开。 必填项已用*标注