首页 > Python > Python使用setup.py进行简单模块打包

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

转载随意~请带上教程地址吧^^

标签:模块 打包

相关文章

评论已关闭