如果想编写一个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 sdistpython 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编辑器上还是会有提示说找不到,不太友好,但是可以免除维护两套代码的烦恼。
转载随意~请带上教程地址吧^^