Python使用setup.py进行简单模块打包
如果想编写一个python模块,分发给别人使用,可以使用setup.py实现模块化。
首先在当目录下新建一个setup.py文件对模块进行描述:
from distutils.core import setup setup(name='shuiguangutils', version="1.0", description="shuiguang 's utils", author="shuiguang", author_email="", py_modules=["utils.StringUtils"], )
上面我使用包名.模块名定义了一个模块,当然可以定义多个包,在包下可以定义多个模块,目录结构如下:
|—— setup.py |—— suba | |——aa.py | |——bb.py | |__ __init__.py | |__ subb |—— cc.py |—— dd.py |__ __init__.py
假设我把StringUtils.py放到aa.py的位置,那么还需要在suba包下的__init__.py下引入StringUtils,__init__.py内容如下:
from . import StringUtils
比如我将常用的字符串函数放到一个StringUtils.py类中:
# -*- coding: utf-8 -*- import sys if sys.version_info[0] == 2: from urlparse import urlparse else: from urllib.parse import urlparse import re class StringUtils(): @classmethod def isEmpty(cls, haystack): if haystack is None: return True elif len(haystack) == 0: return True else: return False
接下来使用命令行将刚才写的模块进行编译打包:
python setup.py build running build running build_py creating build creating build/lib creating build/lib/utils copying utils/__init__.py -> build/lib/utils copying utils/StringUtils.py -> build/lib/utils
执行之后会在同级目录下生成一个dist目录,使用tree命令查看层级发现:
. ├── MANIFEST ├── __init__.py ├── build │ └── lib │ └── utils │ ├── StringUtils.py │ └── __init__.py ├── setup.py └── utils ├── StringUtils.py └── __init__.py
上面的命令只是将源码拷贝到build目录下,还需执行另一条命令对build目录进行打包:
python setup.py sdist
python setup.py sdist running sdist running check warning: check: missing required meta-data: url warning: sdist: manifest template 'MANIFEST.in' does not exist (using default file list) warning: sdist: standard file not found: should have one of README, README.txt writing manifest file 'MANIFEST' creating shuiguangutils-1.0 creating shuiguangutils-1.0/utils making hard links in shuiguangutils-1.0... hard linking setup.py -> shuiguangutils-1.0 hard linking utils/StringUtils.py -> shuiguangutils-1.0/utils hard linking utils/__init__.py -> shuiguangutils-1.0/utils creating dist Creating tar archive removing 'shuiguangutils-1.0' (and everything under it)
再次执行tree命令:
. ├── MANIFEST ├── __init__.py ├── build │ └── lib │ └── utils │ ├── StringUtils.py │ └── __init__.py ├── dist │ └── shuiguangutils-1.0.tar.gz ├── setup.py └── utils ├── StringUtils.py └── __init__.py
发现多了一个dist目录,就是刚才打好的包,把这个文件传给别人安装即可。
安装模块也很简单,只需要将此tar.gz的包解压后,使用命令安装即可:
python setup.py install running install running build running build_py creating build creating build\lib creating build\lib\utils copying utils\__init__.py -> build\lib\utils copying utils\StringUtils.py -> build\lib\utils running install_lib creating C:\Python27\Lib\site-packages\utils copying build\lib\utils\StringUtils.py -> C:\Python27\Lib\site-packages\utils copying build\lib\utils\__init__.py -> C:\Python27\Lib\site-packages\utils byte-compiling C:\Python27\Lib\site-packages\utils\StringUtils.py to StringUtils.pyc byte-compiling C:\Python27\Lib\site-packages\utils\__init__.py to __init__.pyc running install_egg_info Writing C:\Python27\Lib\site-packages\shuiguangutils-1.0-py2.7.egg-info
这样就将你的模块安装到别人的全局环境中去了。
我们写一个简单的测试,看看有没有安装成功:
from utils.StringUtils import * print StringUtils.isEmpty("a") print StringUtils.isEmpty("")
在命令行中同样也可以使用以上方式进行导入StringUtils类下的所有方法:
>>> from utils.StringUtils import * >>> StringUtils.isEmpty("a") False >>> StringUtils.isEmpty("") True
在函数式编程中,如果我们将函数直接写在文件中,而不是封装到某一个类里面,比如:在StringUtils中添加一个全局函数:
def strReplaceOnce(haystack, left='', right=''): return haystack.replace(left, right, 1)
类似这样,导入方式和上面的方式一样:
from utils.StringUtils import * print strReplaceOnce("aba", "b", "c")
事实上,函数式编程中我们不推荐使用import *导入所有的方法,可以在StringUtils.py里定义一个__all__变量:
__all__ = ["StringUtils", "strReplaceOnce"]
使用__all__变量可以控制要暴露出去的方法,没有暴露的函数是不能被访问到的,即使是之前定义的那个StringUtils类(python允许一个文件里允许写多个类和全局函数,文件名可以任意),一旦你定义了__all__全局变量,你就必须考虑到哪些东西是需要暴露给import *。
问题:使用模块打包安装解决了软件的分发问题,但是作为项目的依赖模块,每次都需要修改模块代码之后,打包安装才能更新到最新代码吗?
目前,我使用的方式是动态导入方式。
在我的工程中,模块(多个)与我的业务代码在同一个工程下,目录结构大概如下:
├── middlewares.py ├── pipelines.py ├── pos.py ├── settings.py ├── shuiguang │ ├── MANIFEST │ ├── __init__.py │ ├── build │ │ └── lib │ │ └── utils │ │ ├── StringUtils.py │ │ └── __init__.py │ ├── dist │ │ ├── shuiguangutils-1.0 │ │ │ ├── PKG-INFO │ │ │ ├── build │ │ │ │ └── lib │ │ │ │ └── utils │ │ │ │ ├── StringUtils.py │ │ │ │ └── __init__.py │ │ │ ├── setup.py │ │ │ └── utils │ │ │ ├── StringUtils.py │ │ │ └── __init__.py │ │ └── shuiguangutils-1.0.tar.gz │ ├── setup.py │ └── utils │ ├── StringUtils.py │ └── __init__.py
平时使用相对路径来定位依赖:
pipelines.py # 获取当前执行脚本的位置 pwd = sys.path[0] # 加载我的扩展工具包 shuiguang = os.path.join(os.path.abspath(pwd), "shuiguang") os.sys.path.append(shuiguang + os.path.sep + "utils") StringUtils = __import__("StringUtils") from StringUtils import * print StringUtils.isEmpty("")
也就是说,我在middlewares.py和pipelines.py中引用我的模块时是这样的:
关键是需要在python中指定Working Directory(工作空间)
工作空间是当前目录很好将我的所有模块append到环境中,如果是同级子目录spiders下的脚本,导入方式是一样的:
spiders/crx.py # 获取workspace位置 pwd = os.path.abspath('.') # 加载我的扩展工具包 shuiguang = os.path.join(os.path.abspath(pwd), "shuiguang") shuiguang = os.path.join(os.path.abspath(pwd), "shuiguang") os.sys.path.append(shuiguang + os.path.sep + "utils") StringUtils = __import__("StringUtils") from StringUtils import * print StringUtils.isEmpty("")
动态导入在IDE编辑器上还是会有提示说找不到,不太友好,但是可以免除维护两套代码的烦恼。
永久地址:http://blog.zhengshuiguang.com/python/module.html
转载随意~请带上教程地址吧^^
评论已关闭