1. 包的安装管理与分发
现代的编程场景早已从单打独斗的个人行为转向了多人合作集体行为.而现代编程语言也都有模块化支持以适应模块化的项目编程.python作为一门历史悠久的现代编程语言拥有让许多优秀的第三方包.现如今多数优质的第三方模块都注册在pip
上可以很方便的下载安装.而github的兴起也促进了python社区的繁荣,每年都会有很多新的优秀的第三方模块进入pythoner的视野中.
历史悠久的同样意味着历史包袱.与javascript的npm相比,pip并不优秀,而由于python的版本割裂问题,也造成了许多第三方库无法向后兼容,但由于python语言尽量坚持一致性原则和实用至上的理念,python的第三方包相对整体质量更高也更易于定制.因此可以说python的包管理在现今看来依然是实用高效的.
python方便的模块引入语法简洁实用,配合第三方库可以让用户有着如同玩乐高积木一般的优秀体验.javascript的ES6语法中模块引入语法很大程度上借鉴了python的模块语法.
本文需要先了解
1.1. 安装包的方法:
python安装包其实很简单,只要将包下载好放入sys.path
可以查找到的路径即可.社区为了统一,封装,自动化这一过程,规定了两种用于安装包的文件setup.py
和setup.cfg
以及与这套规范配套的标准库工具distutils
,而后随着社区的发展,一个对distutils
进行封装扩展的新工具setuptools
诞生了,现在setuptools
基本已经成为了标准库工具distutils
的替代品.
1.1.1. 安装定义文件(setup.py)
安装脚本setup.py
就类似npm的package.json
,它负责设定包的基本信息和依赖,以下是一个官方的例子的改版 setup.py:
# 一般用setuptools from setuptools import setup, find_packages,Command # 维持不同平台文件相同的编码 from codecs import open import distutils from os import path import os import subprocess here = path.abspath(path.dirname(__file__)) # 用同文件夹下的README.rst文件定义长介绍 with open(path.join(here, 'README.rst'), encoding='utf-8') as f: long_description = f.read() # 用同文件夹下的requirements.txt文件定义运行依赖 with open(path.join(here, 'requirements.txt'), encoding='utf-8') as f: REQUIREMETS = f.readlines() packages=find_packages(exclude=['contrib', 'docs', 'test']) class CoverageCommand(Command): description = "覆盖率" user_options = [ ("output=","o","选择报告的输出方式") ] def initialize_options(self): self.cwd = None self.output = '' def finalize_options(self): self.cwd = os.getcwd() if self.output and self.output not in ("report","html"): raise Exception("Parameter --output is missing") def run(self): assert os.getcwd() == self.cwd, 'Must be in package root: {self.cwd}'.format(self=self) command = ['/usr/bin/env', 'python', '-m', 'coverage'] if self.output: command.append('{self.output}'.format(self=self)) else: command.append('report') self.announce('Running command: {command}'.format(command = str(command)), level=distutils.log.INFO) subprocess.check_call(command) class TestCommand(Command): description = "测试" user_options = [] def initialize_options(self): self.cwd = None def finalize_options(self): self.cwd = os.getcwd() def run(self): assert os.getcwd() == self.cwd, 'Must be in package root: {self.cwd}'.format(self=self) command = ['/usr/bin/env', 'python', '-m', 'coverage','run' ,'--source=score_card_model', '-m', 'unittest', 'discover', '-v', '-s', 'test'] self.announce('Running command: {command}'.format(command = str(command)), level=distutils.log.INFO) subprocess.check_call(command) setup( name='score_card_model', version='0.0.1', description='A sample Python project', long_description=long_description, # 项目地址 url='https://github.com/pypa/sampleproject', # 作者信息 author='The Python Packaging Authority', author_email='[email protected]', # 维护者信息 maintainer = "", maintainer_email = "", # 指定可用的平台,一般有c扩展的可能会用到 platforms = ["any"], # 许可证信息 license='MIT', # 分类信息,具体看 https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ # 发展时期,常见的如下 # 3 - Alpha # 4 - Beta # 5 - Production/Stable 'Development Status :: 3 - Alpha', # 开发的目标用户 'Intended Audience :: Developers', # 属于什么类型 'Topic :: Software Development :: Build Tools', # 许可证信息 'License :: OSI Approved :: MIT License', # 目标python版本 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', ], # 关键字 keywords='sample setuptools development', # 指定用到的模块,find_packages会找到同文件夹下的模块,用`exclude`指定排除的模块 packages=packages, # 运行时使用的依赖 install_requires=REQUIREMETS, # 是否支持直接引用zip文件,这是setuptools的特有功能 zip_safe=False, # 额外环境的依赖,一般不单独用文件指出 # for example: # pip install -e .[dev,test] # extras_require={ # 'dev': ['check-manifest'], # 'test': ['coverage'], # }, # 指定可执行脚本,如果安装,脚本会被放到默认安装路径 #scripts=["scripts/test.py"], # 模块如果有自带的数据文件,可以用package_data指定 #package_data={ # 'sample': ['package_data.dat'], #}, # 指定模块自带数据所在的文件夹 data_files=[('./', ['requirements.txt'])], # 定义自定义命令 cmdclass = { 'coverage':CoverageCommand, 'test':TestCommand } )
1.1.2. *关于版本号
pep-440定义了符合python规范的版本号格式.当然了这不是强制要求,你要是喜欢也可以按npm的规范定义版本号,或者自己定义一套规范.最重要的是不能有歧义而且便于管理.当然了更加推荐按照pep-440
的规范定义版本号.
1.1.3. *定义setup.py的子命令
类似package.json
,setup.py也可以定义子命令,就是相比node.js的要稍微麻烦些.需要继承setuptools.Command
, 需要重写其中的元素:
description
描述字符串user_options
保存子参数设置的列表,每个元素为一个3元的元组,第一位为全称(--xxx
),第二位为简称(-o
),第三位为描述文字
[("output=","o","选择报告的输出方式")]
其中的方法
- initialize_options(self) 初始化子参数
self.output = ''
- finalize_options(self) 判断命令行与设置的匹配与否和后续操作
if self.output and self.output not in ("report","html"): raise Exception("Parameter --output is missing")
- run(self) 运行逻辑
assert os.getcwd() == self.cwd, 'Must be in package root:
1.1.4. 安装包
在上面的步骤定义完成后只要执行python setup.py install
就会将这个包安装到python
安装目录下的site-packages
文件夹中
1.2. 包管理
python最常用的包管理工具就是官方的pip,当然Anaconda的conda命令也可以作为包管理工具使用,而且可能更加方便一些,但其实conda命令下载安装python的包也是用的pip.
1.2.1. pip
pip是python的官方第三方包管理工具(PEP 453),收录了大部分的第三方包。多数自带python的系统如mac osx, ubuntu都已经有现成的pip安装着了。如果确实没有pip可以去https://pip.pypa.io/en/latest/installing.html#python-os-support 下载get-pip.py
文件,下载到本地后,cd到同一文件夹下使用python get-pip.py安装.基本上不会有人不装pip,因为如果不用它,python就少了很多便利性
pip基本使用:
pip命令可以单独作为脚本命令使用如pip list
,也可以配合python解释器使用python -m pip list
后一种方式的好处是可以在不同的python环境使用pip,pip会自己把模块安装到指定python的第三方包文件夹下
安装模块
pip install packageName
下载并安装最新的版本pip install packageName==1.0.0
下载并安装指定版本pip install 'packageName>=1.0.0
下载并安装至少某个版本以上的版本的包pip install url
#从指定网址资源安装pip install path
#指定本地位置安装pip install --find-links=url
从指定url下载安装pip install --find-links=path
从指定path下载安装pip install --upgrade packageName
更新一个已经安装过的过期模块
从需求文件安装模块
pip freeze > requirements.txt
将当前pip管理的模块信息存储进文本文件pip install -r requirements.txt
从文本文件安装依赖的模块
卸载
pip uninstall <packageName>
查找
pip search <name>
查看模块信息
pip show <packageName>
查看pip管理了哪些模块
pip list
pip list --outdated
查看过期的模块
1.2.2. 配置pip
pip的配置通常保存在~/.pip/pip.conf
中,如果在你的机器上这个路径不存在,你可以自己创建进行配置.
- pip的国内源设置
感谢天朝的伟大电子长城,我们很多时候无法练到pypi的服务器,还好国内豆瓣有个一直在维护的镜像站可以提供源作为替代
如何设置呢?
在你的pip的配置文件中更新上如下内容:
[global] index-url = http://pypi.douban.com/simple trusted-host = pypi.douban.com
- pip的代理设置
另一种方式就是翻墙,翻墙的话就是靠代理了,在你的pip的配置文件中更新上如下内容:
[global] proxy=server:port
1.2.3. 通过pip安装本地包
pip
工具支持直接安装本地的模块,像wheel
打包过的模块就可以直接使用pip安装
pip install somedir/xxxxxx.wheel
1.2.4. conda 包管理工具
Anaconda的定位是数据科学工具箱,它其实并不局限于python.
我们的pip和conda并不冲突,而conda实际上也是依赖于pip工具的,用conda的好处是:
- 有些复杂的安装过程他会帮你省去,
- 可以用它安装一些Anaconda公司的商业工具
- 它对于包版本的追踪更加细致.
- 可以用它安装一些不是python包的工具,尤其一些C/C++工具,比如windows下的minwg.
和pip一样,conda list
是查看已安装包信息的工具
而查找包还是conda search <pkgname>
要安装也还是conda install
,只是它可以加上参数--name <envname>
来为特定环境跨环境安装包
而删除包就和pip有所不同了,它使用的是conda remove <pkgname>
命令.
1.3. *包分发
无论使用哪种方式管理python的第三方模块,如果想将自己的包分发出去与别人共享,都应该使用官方的pypi平台.和npm一样,作为开发者,你需要先注册才可以上传代到代码库.注册的时候注意,password
必须大于16位,PGPkeyID
可以不填. 表单提交好后登入邮箱验证即可注册完成.
1.3.1. 将包注册到pypi服务器
完成pypi上的注册,并定义好setup.py
脚本后就可以将自己写的模块上传到pypi服务器上了.
注册包
cd到 项目根目录
python setup.py register
用刚才注册的信息来注册本台电脑
注意直接这样会有可能报错,因为和原来有个名字太接近了.
我们应该先检查下名字
pip search <pkgname>
用来查看有哪些相关的包,我们得确定没有重名
上传
[("output=","o","选择报告的输出方式")]
0
1.3.2. 分发模块
setuptools
支持的分发格式可以在python官网查看,打包方式都是使用
[("output=","o","选择报告的输出方式")]
1
1.4. *使用wheel分发模块
本部分主要以wheel
为例.
wheel
是官方钦定的包分发格式,它本质上是一个zip包,使用.whl
作为扩展名.wheel
包的分发模式并不是为中心化的pypi设计的,而是为了方便点对点的传播,因此wheel
模块更多的是本地安装
1.4.1. 现有支持wheel的模块:
现有支持wheel的模块已经不少,而且很多wheel是为了针对不同操作系统而额外打包的.pypi官方的wheel支持列表可以在http://pythonwheels.com/找到,而许多科学计算工具在windows下的wheel可以在http://www.lfd.uci.edu/~gohlke/pythonlibs/找到
1.4.2. 将模块打包成wheel
wheel
提供了一个bdist_wheel
作为 setuptools 的扩展命令,这个命令可以用来生成wheel
包。
[("output=","o","选择报告的输出方式")]
2
pip
提供了对wheel
的支持,setup.cfg
可以用来定义wheel
打包时候的相关信息.
1.5. *本地架设pypi服务器
很多时候我们会有这样一种需求,我们希望我们的包私有或者在小范围内传播,这时候我们就可以架设本地的pypi服务器了
本地架设pypi
服务器可以使用pypiserver或者localshop他们用法差不多,不同之处在于前者更轻些,而后者除了可以本地架设pypi服务器外还可以自动镜像pypi的包仓库.
1.6. *工具安利
pmfp
(python3.5以上可用)是我写的一个仿照npm的包管理统一平台,目前在测试阶段,但已经可用,它封装了pip,setuptools,pyvenv,zipapp等工具的操作,并设定了几个常用框架的模板,有兴趣的同学可以尝试使用,顺便帮我找找bug,这个工具我并没有写测试,而是打算用边用边测的形式逐步完善它.希望有同学一起帮我完成这个项目,不胜感激!
1.7. 关于重复造轮子
python提倡任何事务总有一种最好的方式实现,并不鼓励重复造轮子(虽然事实上python重复的轮子相当多,官方与社区也总会有意见不统一的情况发生).自己写一些模块固然可以,但最好还是先看看有没有现成的实现.
还没有评论,来说两句吧...