Python程序设计开发宝典
上QQ阅读APP看书,第一时间看更新

1.8 编写与发布自己的包

除了可以在开发环境中或命令提示符环境中直接运行,任何Python程序文件都可以作为模块导入并使用其中的对象,这也是实现代码复用的重要形式。通过Python程序的__name__属性可以识别程序的使用方式,每个Python脚本在运行时都会有一个__name__属性,如果脚本作为模块被导入,则其__name__属性的值被自动设置为模块名;如果脚本作为程序直接运行,则其__name__属性值被自动设置为字符串"__main__"。例如,假设程序hello.py中的代码如下:

        def main():                        #def是用来定义函数的Python关键字
            if__name__=='__main__':        #选择结构,识别当前的运行方式
                print('This program is run directly.')
            elif__name__=='hello':         #冒号、换行、缩进表示一个语句块的开始
                print('This program is used as a module.')

        main()                             #调用上面定义的函数

那么通过任何方式直接运行该程序时都会得到下面的结果:

     This program is run directly.

而在使用import hello导入该模块时,得到结果如下:

     This program is used as a module.

很明显,对于大型软件的开发,不可能把所有代码都存放到一个文件中,那样会使得代码很难维护。对于大型软件系统,可以使用包来管理多个模块。包是Python用来组织命名空间的重要方式,可以看作是包含大量Python程序模块的文件夹。在包的每个子文件夹中都必须包含一个__init__.py文件,该文件可以是一个空文件,仅用于表示当前文件夹是一个包。__init__.py文件的主要用途是设置__all__变量以及执行初始化包所需的代码,其中__all__变量中定义的对象可以在使用“from…import*”时全部被正确导入。

假设有如下结构的包:

        sound/                       Top-level package
              __init__.py            Initialize the sound package
              formats/               Subpackage for file format conversions
                    __init__.py
                    wavread.py
                    wavwrite.py
                    
              effects/               Subpackage for sound effects
                    __init__.py
                    echo.py
                    surround.py
                    reverse.py
                    
              filters/               Subpackage for filters
                    __init__.py
                    equalizer.py
                      

那么,可以在自己的程序中使用下面的代码导入其中一个模块:

     import sound.effects.echo

然后使用完整路径来访问其中的对象,例如:

     sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

如果sound\effects\__init__.py文件中有下面一行的代码:

     __all__=['echo', 'surround', 'reverse']

那么就可以使用下面的方式来导入__all__变量指定的echo、surround和reverse对象:

     from sound.effects import*

然后使用下面的方式来使用其中的成员:

     echo.echofilter(input, output, delay=0.7, atten=4)

在组织自己的包时,应避免只有一个__init__.py的文件夹。例如,如果可以用common.py实现同样的功能,就不要使用common\__init__.py这样的结构,除非common.py需要实现的功能非常多,需要分散到多个库中,例如common\database.py、common\net.py、common\user.py等,然后再在common\__init__.py中通过__all__变量指定哪些对象可以导入。

在自己编写模块时,可能需要反复修改代码然后重新导入模块进行测试,此时可以使用imp模块或importlib模块的reload()函数。重新加载模块时要求该模块已经被正确加载过,也就是说,第一次导入和加载模块时不能使用reload()方法。

本节的最后介绍如何把自己的Python包发布到pypi,假设有Python程序文件fastcopytree.py,现在编写一个对应的setup.py文件,内容如下:

        from distutils.core import setup


        setup(name='fastcopytree', version='1.0.0', py_modules=['fastcopytree'],
            author='dong fuguo', author_email='dongfuguo2005@126.com',
            url='http://user.qzone.qq.com/306467355/2')

然后在命令提示符环境中执行python setup.py register并选择1进行登录,如果没有pypi账号可以选2先进行注册,登录后再使用python setup.py register-n检查包名是否可用,最后使用python setup.py sdist upload命令把自己的包发布到pypi上去,别人就可以使用pip install fastcopytree来安装这个包了。当然,上面的setup.py程序还有更多的参数可以使用,例如,使用python setup.py bdist_wininst可以创建Windows安装包。