转载:http://zengrong.net/post/2192.htm
本文基于 python 3.4 ,在 python 2.7 上也可以使用。
在昨天的文章 在 setuptools 中使用 dependency_links 里,我提到了发布hhlb 工具集的工作。今天又遇到了一些具体的问题。
hhlb 的文件夹结构如下:
. ├── MANIFEST.in ├── README.rst ├── hhlb │ ├── __init__.py │ ├── __main__.py │ ├── admin.py │ ├── base.py │ ├── bin │ ├── build.conf │ ├── config.py │ ├── init.py │ ├── res.py │ ├── templ.py │ └── update.py ├── requirements.txt ├── setup.py └── test └── testbuild.py
hhlb 是一个 python package。最初的做法,我在 hhlb 的同级目录中放了一个 build.py
文件,在这个文件中 import hhlb
这个 package ,使用其功能。
例如,下面的代码会强制初始化整个项目:
python build.py init -af
build.py
这个引导文件其实 没有存在的必要 。既然我要将 hhlb 发布成一个工具,我就应该让它能像这样被调用:
hhlb init -af python -m hhlb init -af
第一种调用方法,就像 pip 一样。第二种方法,就像 http.server 一样。
另外,在开发过程中,还可以直接通过指定 hhlb 文件夹路径的方式来调用:
python /path/to/hhlb init -af
1. 调用顺序
为了实现上面的功能,我们先需要了解一下 python 对于 package 的调用顺序。
如果你希望 python 将一个文件夹作为 package 对待,那么这个文件夹中必须包含一个名为 __init__.py
的文件,即使它是空的。 参见: Packages
如果你需要 python 讲一个文件夹作为 package 执行,那么这个文件夹中必须包含一个名为 __main__.py
的文件,当执行 python -m hhlb
或者python hhlb
的时候,这个文件中的代码都会被执行。参见: main — Top-level script environment
让我们做个试验。
在 hhlb/__init__.py
中写入如下内容:
print('__init__') print('__init__.__name__', __name__) print('__init__.__package__', __package__)
在 hhlb/__main__.py
中写入如下内容:
print('__main__') print('__main__.__name__', __name__) print('__main__.__package__', __package__)
执行 python hhlb
,打印如下:
-> % python hhlb __main__ __main__.__name__ __main__ __main__.__package__
这说明,将 hhlb 当作文件夹执行的时候,对于 __main__.py
来说,变量__package__
是一个空字符串。而 __init__.py
不会被执行。
下面看看执行 python -m hhlb
的效果:
-> % python -m hhlb __init__ __init__.__name__ hhlb __init__.__package__ hhlb __main__ __main__.__name__ __main__ __main__.__package__ hhlb
当作为模块执行的时候,python 会先执行 __init__.py
,然后执行__main__.py
。而且,前者和后者对于 __name__
变量的理解是不同的。
2. 定义一个 main()
对于一个 package 来说,既然 __init__.py
必须存在,而且当作为模块执行的时候,它会先执行,我们就应该把入口函数 main()
定义在 __init__.py
中。
当我们使用模块方式 -m
调用包的时候,__init__.py
定义了 main()
函数,然后在 __main__.py
中调用它,就能实现我们统一入口的目的。
那么继续试验。
将 hhlb/__init__.py
修改成这样:
print('__init__') print('__init__.__name__', __name__) print('__init__.__package__', __package__) def main(): print('__init__.main()')
在 hhlb/__main__.py
中对 main()
进行调用:
print('__main__') print('__main__.__name__', __name__) print('__main__.__package__', __package__) import hhlb hhlb.main()
注意,在 __main__.py
中,没有必要判断 __name__
是否为 __main__
,因为无论如何调用, __name__
的值都一定是 __main__
。