A. Python项目生成requirements的三种方式
第一种方式适用于通过conda来管理/安装项目依赖包,生成的文件内容如下:
第二种方式适用于通过pip来管理/安装项目依赖包,生成的文件内容如下:
所以看到通过这种方式生成的requirements文件中,通过conda安装的包并不能给出包版本,而是 @ file:////... 这种形式。
如果没有使用虚拟环境,或虚拟环境中有很多项目没有使用的包,则上面两种方式都将环境中的所有包版本记录在requirements文件中,不便于项目移植,这时候需要第三种方式:
上面第一条命令默认安装到全局的环境中
如果上面的命令不能安装到指定Python版本的虚拟环境中,则
可用pycharm,进入命令行,运行:
参考:
python生成requirements.txt的两种方法 python 脚本之家 (jb51.net)
我的项目在conda环境中使用的软件包-python黑洞网 (pythonheidong.com)
pipreqs · PyPI
User Guide - pip documentation v22.1.2 (pypa.io)
B. python和anaconda有哪些区别
1、安装包大小不同
python自身缺少numpy、matplotlib、scipy、scikit-learn等一系列包,需要安装pip来导入这些包才能进行相应运算。
Anaconda(开源的Python包管理器)是一个python发行版,包含了conda、Python等180多个科学包及其依赖项。包含了大量的包,使用anaconda无需再去额外安装所需包。
2、作用不同
Python语法和动态类型,以及解释型语言的本质,使它成为多数平台上写脚本和快速开发应用的编程语言。anaconda可以用于在同一个机器上安装不同版本的软件包及其依赖,并能够在不同的环境之间切换。
3、性质不同
Anaconda是一个打包的集合,里面预装好了conda、某个版本的python、众多packages、科学计算工具等等,所以也称为Python的一种发行版。
Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。
C. 怎样将python程序及其依赖打包成rpm包部署
可以的,虽然说pip比较流行,但rpm从原理上完全可以覆盖这些部署过程,只有一些小的要求:
所有环境的Python安装方式必须一致,不能存在某些Python是2.6,某些是2.7,某些装在/usr/local下面,某些在/usr/lib下面之类的情况
如果带有C扩展的话,链接到的库应当是来自某个rpm包的。如果没有的话就简单多了。
如果有依赖的其他Python包的话,需要把依赖的包也做成rpm;实在偷懒也可以打进同一个rpm中。
实际上发行版中有许多Python库都以rpm的形式提供,不过一般我们嫌它版本旧,更倾向于用pip管理一组新的。
要写一个rpm spec,可以从头自己写,也可以参考一些其他软件的spec,我建议你采用后者,既然你们公司已经广泛使用rpm了,那肯定有很多本公司的spec可以参考,也有人可以问。
回到话题,写rpm spec,或者说rpmbuild的过程,分成以下步骤:
部署源代码,一般来说将一个.tar.gz复制到SRC目录中,然后用%setup宏就可以了,这个宏也基本上没干什么特别的事,就是把.tar.gz解压缩了一下,然后进入相应目录。.tar.gz只要从git中取出干净的源代码,整个目录打成tar包就可以了。好像还有专门的git-archive命令。
build过程,对应C/C++项目中的./configure, make。对Python项目来说一般可以跳过,因为setup.py都会搞定。也可以选择在这个阶段使用setup.py进行build,build出一个wheel包来,这是我推荐的方法,因为wheel包和直接setup.py install的目录结构有一些差异,而pip安装默认是按照wheel的方式。也就是说在这一步调用:setup.py bdist_wheel。再精细一点可以指定build的目标为build目录,这样需要cleanup的时候容易一些。
install过程,对应C/C++项目中的make install。一般来说我们平时怎么装这个软件,这一步就怎么装,所以我们也是使用setup.py。唯一的技巧在于,我们需要指定安装的目标到rpmbuild根的某个目录下面,而不是系统的目录。这个目录在install开始之前应当被清空。可以参考其他spec。对于我们来说,就是将Python的库安装到指定的目录,而不是系统的site-packges当中。如果你在build这一步已经打包了wheel,那么使用wheel install命令、指定目标文件夹就可以了。
打包过程,rpm的打包原理非常简单,最开始install的目录是空的,install完成后里面有了一堆文件,那么就按照文件列表将这一堆文件打包、然后指定每个文件将来应当安装到什么位置。对于Python项目来说,一般会安装package名的目录和EGGINFO两个目录,将这两个目录连同里面所有的内容一起加入%files段就可以了。如果你要部署的是某个应用,一般你还会希望将init.d中的启动脚本、或者 systemd的配置文件以及其他应用的配置文件一起部署了,可以在install的过程中将这些文件从源文件目录中复制到目标文件夹里,然后加到files段里面。
安装、卸载脚本。表现为%pre, %post之类的段一般来说如果你有服务要安装的话,需要在这里使用chkconfig, chkconfig on,或者systemd的话就是systemctl enable。如果你希望安装完有个机会修改配置文件,可以选择在首次安装的时候不要启动服务,而在update的时候启动服务,这可以通过脚本传进的第一个命令行参数$1进行判断,0、1、2表示不同的情况(分别是卸载、安装、更新,具体的记不清了),可以参考其他人写的spec;也可以选择安装完永远直接启动,在post中使用service xxx start, 在preuninstall的时候使用service xxx stop。
rpm的功能还是很强大的,除了跟pip共通的功能以外,它有一个显着的好处就是可以帮助你同时管理服务的配置、启动和停止,从而简化部署过程。不过要注意如果使用rpm的话就不要同时使用pip,否则pip更新过的库,rpm卸载或更新时会冲突。
有第三方依赖的时候,一种方法是为每个依赖项写一个独立的spec,里面内容都是上面这样的直接调用setup.py,或者更简单一些,在install的时候直接调用pip就可以;然后在需要这些依赖项的rpm包的spec当中写上Require信息,跟pip体系的requirements差不多。偷懒也可以在同一个rpm中打了一堆Python包进去,但是如果有多个独立安装的rpm都打了同一个Python包,就会冲突,要注意。
其实用习惯了会觉得也不比pip差,需要跟非Python组件混合部署的时候反而会觉得简单了很多。
D. pyenv下python依赖的路径和打包项目
一个很好用的打包工具:pyinstaller
很简单,cd到目标项目的.py目录下,运行 pyinstaller -F file.py -F:表示只生成一个可执行文件,如果不加则会在dist文件夹下生成很多文件和一个可执行文件。
successfully之后,当前目录下会生成两个文件夹,一个dist,一个build,我们所需要的文件在dist里面。
直接运行就可以了
为啥不用这个方法呢,因为在python的包中,有的完全是python的, 而有的并不是纯python的 ,那么接下来介绍方法二,如何安全的提取打包。
我是从pycharm中找到了蛛丝马迹
现在全都找到了,如果你可以打开.py文件,那么路径更好找,直接cd过去就ok了:
回到之前我们的疑问: 怎么判断安装包是不是纯python的呢?
这里面的.so文件就不是python的文件,.py/.pyc是。所以这种包无法打包,只能到目标环境下手动安装。
找到我们需要的包,cp出去放到 sys.path 可以找到的路径下就可以了(放到你要run的那个文件夹里),结果如下:
重新创建一个虚拟环境,运行下export.py查看能否正常运行就ok了。
E. python多接口有依赖如何调用
方法如下:
1. 对于单接口测试如果依赖接口只需要在测试开始执行一次,那么可以将依赖接口的请求放在类级前置方法中,然后通过全局变量或者当前用例类属性来传递依赖数据。
2. 对于单接口测试如果依赖接口需要在每个用例前执行,那么可以将依赖接口的请求放在方法级前置方法中,然后通过用例对象属性来传递依赖数据
3. 对于多接口的业务流测试,可以将下一个接口需要依赖的数据通过当前用例类属性来传递依赖数据。
Python由荷兰数学和计算机科学研究学会的Guido van Rossum 于1990 年代初设计,作为一门叫做ABC语言的替代品。 Python提供了高效的高级数据结构,还能简单有效地面向对象编程。Python语法和动态类型,以及解释型语言的本质,使它成为多数平台上写脚本和快速开发应用的编程语言,随着版本的不断更新和语言新功能的添加,逐渐被用于独立的、大型项目的开发。
F. Linux 下Python 脚本编写的"奇技淫巧"
“ 生命完美的答案,无非走过没有遗憾 ---《天蓝》”
“如何能够解析脚本运行命令行选项(位于 sys.argv 中)”
argparse 模块可被用来解析命令行选项
常用来定义一个脚本的说明文档,一般我们写python脚本会通过 if..else 的方式来提供一个脚本说明文档,python不支持switch。所以很麻烦,其实,我们可以通过 argparse 来编写说明文档。
我们来看看怎么执行一个python脚本
对于熟悉Linux的小伙伴下面的文档在熟悉不过了,这个一个标准Linxu软件包的说明文档,文档中定义是软件包的说明
来看看这个脚本是如何编写的
为了解析命令行选项, 首先要创建一个 ArgumentParser 实例, 使用 add_argument() 方法声明你想要支持的选项。在每个 add-argument() 调用中:
dest 参数指定解析结果被指派给属性的名字。 metavar 参数被用来生成帮助信息。
action 参数 指定跟属性对应的处理逻辑,通常的 值为 store , 被用来存储 某个值 或将 多个参数值收集到一个列表中 。
nargs 参数收集 所有剩余的命令行参数到一个列表中。在本例中它被用来构造一个文件名列表
action='store_true' 根据参数是否存在来设置一个位置 Boolean 标志:
action='store' 参数接受一个单独值并将其存储为一个字符串
如果一个都没有,会提示缺少参数 -p/--pat
choices={'slow', 'fast'}, 参数说明接受一个值,但是会将其和可能的选择值做比较,以检测其合法性:
一旦参数选项被指定,你就可以执行 parser.parse() 方法了。它会处理 sys.argv 的值并返回一个结果实例。每个参数值会被设置成该实例中 add_argument() 方法的 dest 参数指定的属性值。
还很多种其他方法解析命令行选项。可以会手动地处理 sys.argv 或者使用 getopt 模块 。但是,如果你采用本节的方式,将会减少很多冗余代码,底层细节 argparse 模块 已经帮你处理好了。你可能还会碰到使用 optparse 库解析选项的代码。尽管 optparse 和 argparse 很像 ,但是后者更先进,因此在新的程序中你应该使用它。
“你写了个脚本,运行时需要一个密码。此脚本是交互式的,因此不能将密码在脚本中硬编码,而是需要弹出一个密码输入提示,让用户自己输入。”
Python 的 getpass 模块 正是你所需要的。你可以让你很轻松地弹出密码输入提示,并且不会在用户终端显示密码。
代码中 getpass.getuser() 不会弹出用户名的输入提示。它会根据该 用户的 shell 环境 或者会依据 本地系统的密码库 (支持 pwd 模块的平台)来使用 当前用户的登录名
在bash中编写pytohn脚本接收外部数据的方式,一般情况下,对于一般变量,我们用命令行变量的方式比较多(手动的处理 sys.argv ),对于 文件内容或者bash命令输出 直接通过脚本内部获取需要的数据。
其实python 脚本也可以用其他方式来接收 传递给他的 文件数据或者bash命令输出 ,包括将 命令行的输出 通过 管道传递 给该脚本、 重定向文件到该脚本 ,或在 命令行中传递一个文件名 或 文件名列表 给该脚本。
这里通过 Python 内置的 fileinput 模块 ,可以实现重 定向,管道,以文佳输出 的方式传递数据到脚本内部
使用 fileinput.input() 方法可以获取当前输入脚本的数据,脚本里面用一个 FileInput 迭代器接收
文件直接接收
重定向接收
管道方式接收
fileinput.input() 创建并返回一个 FileInput 类的实例,该实例可以被当做一个 上下文管理器 使用。因此,整合起来,如果我们要写一个打印多个文件输出的脚本,那么我们需要在输出中包含文件名和行号
“你想执行一个外部命令并以 Python 字符串的形式获取执行结果。”
使用 subprocess.check_output() 函数。
执行下试试
如果被执行的命令以非零码返回,就会抛出异常。下面的例子捕获到错误并获取返回码:
默认情况下, check_output() 仅仅返回输入到标准输出的值。如果你需要 同时收集标准输出和错误输出 ,使用 stderr 参数:
如果你需要用一个超时机制来执行命令,使用 timeout 参数:
通常来讲,命令的执行 不需要 使用到 底层 shell 环境(比如 sh、bash) 。一个字符串行表会被传递给一个 低级系统命令 ,比如 os.execve() 。
如果你想让 命令被一个shell 执行 ,传递一个字符串参数,并设置参数 shell=True . 有时候你想要 Python 去执行一个复杂的 shell 命令 的时候这个就很有用了,比如管道流、I/O 重定向和其他特性。例如:
是在 shell 中执行命令会存在一定的安全风险,特别是当参数来自于用户输入时。这时候可以使用 shlex.quote() 函数 来将参数正确的用双引用引起来。
使用 check_output() 函数 是执行 外部命令 并获取其 返回值 的最简单方式。但是,如果你需要对 子进程做更复杂的交互 ,比如给它发送输入,你得采用另外一种方法。这时候可直接使用 subprocess.Popen 类。
关于子进程,简单来看下
也可以进程列表同协程结合的方式。你既可以在子shell中 进行繁重的处理工作,同时也不会让子shell的I/O受制于终端。
如果直接丢到后台会自动在终端输出IO
subprocess 模块对于依赖 TTY 的外部命令不合适用 。例如,你不能使用它来自动化一个用户输入密码的任务(比如一个 ssh 会话)。这时候,你需要使用到第三方模块了,比如基于着名的 expect 家族的工具(pexpect 或类似的)(pexpect可以理解为Linux下的expect的Python封装、通过pexpect可以实现对ssh、ftp、passwd、telnet等命令行进行自动交互,而无需人工干涉来达到自动化的目的。比如我们可以模拟一个FTP登录时所有交互,包括输入主机地址、用户名、密码、上传文件等,待出现异常还可以进行尝试自动处理。)
“你想向标准错误打印一条消息并返回某个非零状态码来终止程序运行”
通过 python 的 raise SystemExit(3) 命令可以主动抛出一个错误,通过 sys.stderr.write 将命令写到标准的输出端
直接将消息作为参数传给 SystemExit() ,那么你可以省略其他步骤
抛出一个 SystemExit 异常,使用错误消息作为参数,它会将消息在 sys.stderr 中打印,然后程序以状态码 1 退出
“你需要知道当前终端的大小以便正确的格式化输出。”
使用 os.get terminal size() 函数 来做到这一点。
“复制或移动文件和目录,但是又不想调用 shell 命令。”
shutil 模块 有很多便捷的函数可以复制文件和目录。使用起来非常简单
这里不多讲,熟悉Linux的小伙伴应该不陌生。
默认情况下,对于 符号链接 这些命令处理的是它指向的东西文件。例如,如果 源文件 是一个 符号链接 ,那么目标文件将会是 符号链接 指向的文件。如果你只想 复制符号链接本身 ,那么需要指定 关键字 参数 follow_symlinks
tree() 可以让你在复制过程中选择性的忽略某些文件或目录。你可以提供一个忽略函数,接受一个目录名和文件名列表作为输入,返回一个忽略的名称列表。例如:
对于文件元数据信息, 2() 这样的函数只能尽自己最大能力来保留它。 访问时间、创建时间和权限 这些基本信息会被保留,但是 对于所有者、ACLs、资源 fork 和其他更深层次的文件元信息就说不准了
通常不会去使用 shutil.tree() 函数 来执行 系统备份 。当处理文件名的时候,最好使用 os.path 中的函数来确保最大的可移植性
使用 tree() 复制文件夹的一个棘手的问题是对于错误的处理,可以使用异常块处理,或者通过 参数 ignore dangling symlinks=True 忽略掉无效符号链接。
“创建或解压常见格式的归档文件(比如.tar, .tgz 或.zip)”
shutil 模块拥有两个函数—— make archive() 和 unpack archive() 可派上用场,
make archive() 的第二个参数是期望的输出格式。可以使用 get archive formats() 获取所有支持的归档格式列表。
“你需要写一个涉及到文件查找操作的脚本,比如对日志归档文件的重命名工具,你不想在 Python 脚本中调用 shell,或者你要实现一些 shell 不能做的功能。”
查找文件,可使用 os.walk() 函数 ,传一个顶级目录名给它
os.walk() 方法 为我们 遍历目录树 ,每次进入一个目录,它会返回一个 三元组 ,包含 相对于查找目录的相对路径,一个该目录下的目录名列表,以及那个目录下面的文件名列表。
对于每个元组,只需检测一下目标文件名是否在文件列表中。如果是就使用 os.path.join() 合并路径。为了避免奇怪的路径名比如 ././foo//bar ,使用了另外两个函数来修正结果
os.walk(start) 还有跨平台的优势。并且,还能很轻松的加入其他的功能。我们再演示一个例子,下面的函数打印所有最近被修改过的文件:
打印10分钟之前被修改的数据
“怎样读取普通.ini 格式的配置文件?”
configparser 模块 能被用来读取配置文件
编写配置文件
如果有需要,你还能修改配置并使用 cfg.write() 方法将其写回到文件中
“你希望在脚本和程序中将诊断信息写入日志文件。”
python 脚本打印日志最简单方式是使用 logging 模块
五个日志调用( critical(), error(), warning(), info(), debug() )以降序方式表示不同的严重级别。 basicConfig() 的 level 参数是一个 过滤器 。所有级别低于此级别的日志消息都会被忽略掉。每个 logging 操作的参数是一个消息字符串,后面再跟一个或多个参数。构造最终的日志消息的时候我们使用了 % 操作符来格式化消息字符串。
如果你想使用配置文件,可以像下面这样修改 basicConfig() 调用:
logconfig.ini
在调用日志操作前先执行下 basicConfig() 函数方法 ,可以找标准输出或者文件中输出
basicConfig() 在程序中只能被执行一次。如果你稍后想改变日志配置,就需要先获取 root logger ,然后直接修改它。
更多见日志模块文档https://docs.python.org/3/howto/logging-cookbook.html
“你想给某个函数库增加日志功能,但是又不能影响到那些不使用日志功能的程序。”
对于想要执行日志操作的函数库,你应该创建一个专属的 logger 对象,并且像下面这样初始化配置:
使用这个配置,默认情况下不会打印日志,只有配置过日志系统,那么日志消息打印就开始生效
通常来讲,不应该在函数库代码中 自己配置日志系统 ,或者是已经有个已经存在的日志配置了。调用 getLogger( name ) 创建一个和调用模块同名的 logger 模块 。由于 模块 都是唯一的,因此创建的 logger 也将是唯一 的。所以当前进程中只有一个logging会生效。
log.addHandler(logging.NullHandler()) 操作将一个 空处理器 绑定到刚刚已经创建好的 logger 对象 上。一个空处理器默认会忽略调用所有的日志消息。因此,如果使用该函数库的时候还没有配置日志,那么将不会有消息或警告出现。
在这里,根日志被配置成仅仅 输出 ERROR 或更高级别的消息 。不过, somelib 的日志级别被单独配置成可以输出 debug 级别的消息, 它的优先级比全局配置高。像这样更改单独模块的日志配置对于调试来讲是很方便的,因为你无需去更改任何的全局日志配置——只需要修改你想要更多输出的模块的日志等级。(这个还有待研究)
“你想记录程序执行多个任务所花费的时间”
time 模块 包含很多函数来执行跟时间有关的函数。尽管如此,通常我们会在此基础之上构造一个更高级的接口来模拟一个计时器。
这个类定义了一个可以被用户根据需要启动、停止和重置的计时器。它会在elapsed 属性中记录整个消耗时间。下面是一个例子来演示怎样使用它:
这里通过 __enter__,__exit__ ,使用 with 语句 以及上下文管理器协议可以省略计时器打开和关闭操作。(关于上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明 __enter__和__exit__方法, , __enter__ 在出现with语句被调用, __exit__ 在代码执行完毕被调用,可以参考open()方法)
在计时中要考虑一个 底层的时间函数问题 。 一般来说, 使用 time.time() 或 time.clock() 计算的时间精度因操作系统的不同会有所不同。而使用 time.perf_counter() 函数可以确保使用系统上面 最精确的计时器 。
“你想对在 Unix 系统上面运行的程序设置内存或 CPU 的使用限制。”
resource 模块 能同时执行这两个任务。例如,要限制 CPU 时间,下面的代码在windows平台执行不了,但是Linux是可以的。
程序运行时, SIGXCPU 信号 在时间过期时被生成,然后执行清理并退出。
这暂时没有好的Demo...
程序运行到没有多余内存时会抛出 MemoryError 异常。
setrlimit() 函数 被用来设置特定资源上面的 软限制和硬限制 。
setrlimit() 函数 还能被用来设置 子进程数量、打开文件数以及类似系统资源的限制(cgroup) 。
“通过脚本启动浏览器并打开指定的 URL 网页”
webbrowser 模块 能被用来启动一个浏览器,并且与平台无关
新窗口打卡网站
当前窗口打开一个tab页
指定浏览器类型,可以使用 webbrowser.get() 函数
G. python和anaconda区别是什么
python和anaconda的区别其实anaconda是包含python的,所以安装了anaconda就不用安装python了。要想跑python程序,要有解释器和编译器。解释器就是python2或python3含有pythonexe,就是将你编写的python语言编译成机器所认识的机器代码。
python和anaconda的不同点
编译器就是你写代码的环境,比如pycharm或者vscode。由于python3不兼容python2,也就是说python2语言写出来的代码没办法在python3的环境中运行。那在装了python3环境的电脑上跑python2怎么办呢,也不能每次跑的时候都重新配一下环境变量吧。
也可以但是不觉得麻烦吗,所以anaconda就诞生了。它可以将每个开发的程序选用不同的环境,而且开发好的程序所需要的环境就是第三方包独立的打包成来,这样再在另一台电脑上跑该程序时就不用再单独下载包了。
H. 再见 requirements.txt,Python 管理依赖项请用 pip
几年前,当我第一次发现 Python 的 requirements.txt 和虚拟环境时,说实话我感觉真的很棒。
requirements.txt文件是管理依赖项的一种非常有用的方法,但过了一段时间,当你拥有多个子依赖项的依赖项时,开始迷失在它们的关系中。
让我们通过一个简单的例子来解释使用它的主要问题。
你安装依赖项 F,它恰好具有 G 作为子依赖项。一段时间后,在 requirements.txt 中看到的只是 A、B、C、D、E、F、G、H 作为你的依赖项。
你不知道直接或间接安装了哪些依赖项,因此现在更新甚至删除 F 成为一个问题,因为你必须搜索才能知道 G 是作为子依赖项安装的。即使搜索,可以删除 G,还是其他包的子依赖项?
最终会留下陈旧的依赖项并堆积垃圾或花费大量时间进行完整搜索并删除所有未使用的内容。
这不是唯一的问题。如果你不够小心并且只是为了测试目的而安装了一个包,那也可能会永远出现在你项目的依赖项中。
为了解决这个问题,许多聪明人想出了更好的方法来管理 Python 中的依赖项。我们有 pip-tools 和 poetry。
在本文中,我们将分享如何使用 pip-tools 的一个可靠示例,说明如何从项目开始就管理依赖项。
假设我们有一个项目,我们必须在其中使用 Django 和 Pandas。
首先,您需要在您将使用的每个 virtualenv 中安装 pip-tools,但它就像使用一样简单:
现在,我们创建一个 requirements.in 文件,并且只包含我们项目的直接依赖项。每次想要更新或包含依赖项时,你都必须来到这里。它看起来像这样:
你能看出它与旧的.txt文件有何不同吗?在这一个中,我们只包括直接依赖项。您可以选择是否包含版本限制,如下所示:
主要区别在于,这是从现在开始生成你的 requirements.txt 的秘诀。完成创建后,你可以运行以最终编译需求:
这将在生成 .in 文件的同一文件夹中生成一个 requirements.txt。但是你会发现他们两个之间有很大的不同。requirements.txt现在通过在它们下面附加该信息来精确控制某个包是否是其他包的子依赖项,如下所示:
就这样,您消除了之前讨论的所有问题。因为你拥有管理包裹所需的所有信息。
除此之外,pip-tools 还提供了其他便利功能,你可以 探索 这些功能以使流程更加轻松。
要升级包,请使用以下命令(以 Django 为例):
这将自动更新你 requirements.txt 文件,包括这些更改。只需记住删除"小于版本4"的约束以允许它。
为了使你的 virtualenv 与当前的 requirements.txt 文件同步,可以简单地运行以下命令:
这将安装、升级或卸载您的 requirements.txt 文件中未包含的任何包。
I. 如何管理python项目
Virtual Environments
首先Python似乎没有类似Maven/Ant这样的项目管理工具。那么当一台机器上有多个python项目,且这些python项目各自有不同的依赖,不想互相干扰时怎么办呢?
官方做法是使用Virtual Environments将每个项目互相隔离开。一般情况下,我们使用python解释器运行python脚本或mole:
>python myScript.py
运行的目录就是安装的python解释器,即python.exe所在的目录。而Virtual Environments就是给每个项目都生成一个项目独有的目录,这个目录里包含python解释器,python标准类库和其他各式各样的必要文件。这样每个项目就可以使用不同的解释器和类库,且互不干扰。
创建过程也很简单。首先找到pyvenv.py所在的目录,这个文件通常在安装目录的自目录\Tools\scripts下。这是一个生成Virtual Environments的工具。然后运行:
>pyvenv tutorial-env
运行后就会生成一个名为“tutorial-env”的目录。找到这个目录,可以发现正如官网所说,这个目录包含运行python项目所必须的一切文件。使用在各自Virtual Environments目录里包含的解释器来运行特定的python项目就可以了。同时,对于那些每个项目使用的特定的依赖(packages或mole),则加入到各自Virtual Environments目录的类库子目录中就可以了。这里需要注意的是。创建完Virtual Environments后,还需要激活。做法很简单,在上例tutorial-env目录下执行下的命令即可:
>tutorial-env/Scripts/activate
PIP
PIP是官方提供的安装python第三方类库(packages/mole)的工具。它可以去PPI(python packages index)查找或下载第三方类库。网址是:https://pypi.python.org/pypi
找到上例Virtual Environments目录下的pip.exe并运行:
>pip install lib_name
即可安装,其他功能请自行查看手册。如果是在python的安装目录下运行pip,则类库可以被非Virtual Environments的所有项目使用。PIP安装的其实是package。
Requirements.txt
在Virtual Environments目录下运行:
>pip freeze > requirements.txt
可以生产一个当前项目所有依赖类库及其版本的list文件,文件名就是requirements.txt(当然也可以用别的名字)。文件内容大致如下:
novas==3.1.1.3
numpy==1.9.2
requests==2.7.0
使用requirements.txt的好处就是:
The requirements.txt can then be committed to version control and shipped as part of an application. Users can then install all the necessary packages with “install -r“:
>pip install -r requirements.txt
这样就可以方便的管理项目依赖了。如果不使用requirements.txt,直接使用version control存储Virtual Environments目录,其他程序员直接下载该目录就可以开始项目开发的做法也可以。
J. 虚拟环境详解
假想您有一个应用程序需要版本1的numpy库,而另一个应用程序需要版本2。如何使用这两个应用程序呢?2.如果您将所有内容安装到python3.6的site-packages,很容易出现这样的情况:您无意中升级了不应该升级的。3.如果要使写的项目正常运行,其所依赖的第三方库的版本更改都可能让这个项目无法正常运行。另外,如果您无法将第三方库安装到全局site-packages目录下,该怎么办呢?例如,在共享主机上。
我们必须考虑到这些场景,所以虚拟环境诞生了!它们有自己的安装目录,并且不与其他虚拟环境共享库,每个虚拟环境都是独立的!
目前,有两种用于创建python虚拟环境的两种工具:
1.venv在python3.3及更高版本中默认可用,并将pip和setuptools安装到创建的虚拟环境中(这种行为只在python3.4及更高版本才会出现)。由于我最近没有时间,我准备在之后的两篇文章中介绍pip和setuptools。
2.而virtualenv需要单独安装。其在python2.7+和python3.3+中默认可用,pip,setuptools和wheel始终安装到创建的虚拟环境中(此行为忽略python版本!)
venv模块支持创建轻量级虚拟环境,该环境具有自己的site-packages,可以选择与系统site-packages隔离。每个虚拟环境都有自己的python二进制文件(与用于创建此环境的二进制文件的版本相匹配),并且可以在其site-packages中安装自己的独立python包。
通过venv命令创建虚拟环境:python3 -m venv /path/virtual/env。运行此命令将创建目标目录(行为为创建任何尚不存在的父目录)并创建一个pyvenv.cfg文件,文件中的home指向运行该命令的python的安装目录。它还创建一个bin(在Windows上是Scripts)子目录,其中包含python二进制文件、二进制文件的符号链接、副本(平台和参数不同,创建的东西也不同)。它还创建一个最初为空的lib/pythonX.Y/site-packages子目录(在Windows上,这是Lib/site-packages)。如果指定了现有目录,则将重新使用现有目录。
不要双击虚拟环境下的python.exe,这样它会忽略虚拟环境。创建的pyenv.cfg文件中的include-system-site-packages默认为false,如果使用了include-system-site-packages选项,则设置为true。除非给出了--without-pip选项,否则将调用ensurepip将pip引导到虚拟环境中。可以为venv提供多条路劲,在这种情况下,将根据给定的选项在每个提供路劲上创建相同的虚拟环境。创建虚拟环境后,可以使用虚拟环境二进制目录中的脚本激活虚拟环境。脚本调用是特定于平台的。在Windows下,虚拟环境目录\Scripts\activate.bat激活虚拟环境。当虚拟环境处于活动状态时,VIRTUAL_ENV环境变量将设置为虚拟环境的路劲,这可用于检查是否运行在虚拟环境中。指的注意的是,你不需要特别激活虚拟环境,激活只是将虚拟环境的二进制目录前置到运行shell的PATH环境变量,以便python调用虚拟环境的python解释器,你可以运行已安装的脚本,而无需使用它们的完整路劲。但是,安装在虚拟环境中的所有脚本都应该可以在不激活它的情况下运行,并自动使用虚拟环境下的python运行。你可以通过在shell中输入"deactive"命令来停用虚拟环境。
当虚拟环境处于活动状态(即,虚拟环境的python解释器正在运行时),属性sys.prefix和sys.exec_prefix指向虚拟环境的基本目录。而sys.base_prefix和sys.base_exec_prefix指向用于创建虚拟环境的python安装目录。如果虚拟环境未处于活动状态,则sys.prefix和sys.base_prefix,sys.exec_prefix和sys.base_exec_prefix都指向非虚拟环境的python安装目录。安装到虚拟环境中的脚本有一行"shebang",它指向虚拟环境的python解释器。这意味着脚本将与该解释器一起运行,而与PATH的值无关。
可以使用下列API根据需要定制虚拟环境:
venv.EnvBuilder( system_site_packages=False , clear=False , symlinks=False , upgrade=False , with_pip=False , prompt=None , upgrade_deps=False )
system_site_packages:指示系统site_packages是否可供虚拟环境使用。
clear:如果为True,将在创建虚拟环境之前删除任何现有目标目录的内容
symlinks:指示是否尝试对python二进制文件进行符号链接而不是复制
upgrade:如果为True,则将更新升级python现有环境,这个选项用于在python已升级到位时使用
with_pip:如果为True,则确保在虚拟环境中安装pip
prompt:激活虚拟环境后要使用的字符串(默认为None,这意味着将使用环境的目录名)。如果是".",当前目录用作提示
upgrade_deps:将基本venv模块更新到pypi上的最新版本
返回的EnvBuilder对象,有以下方法:
craete(envdir),通过指定要包含虚拟环境的目标目录,创建虚拟环境。
ensure_directories( env_dir ),创建环境目录和所有必要的目录,并返回一个上下文对象,这个上下文对象供其他方法使用。
create_configuration(context),在虚拟环境下创建pyenv.cfg配置文件
setup_python( context ),在虚拟环境下创建python可执行文件的拷贝或符号链接,在POSIX系统上,如果python3被使用,创建指向该可执行文件的python和python3符号链接(除非它们已经存在)
setup_scripts( context ),将适合平台的激活脚本安装到虚拟环境中。
upgrade_dependencies( context ),升级虚拟环境中的核心venv依赖项包(当前为pip和setuptools)。
post_setup( context ),可在第三方实现中重写。以在虚拟环境中预安装软件包步骤之后 ,进行自定义的操作
我在想还是先略过他们吧,定制一个venv实在是没有必要,因为虚拟环境的创建还有一个更强大的工具。
下面我们来介绍venv的几种命令参数,相信看了前面,现在看到便不会看不懂了!
--system-site-packages,允许虚拟环境访问系统site-packages目录
--symlinks,在符号链接不是平台的默认操作时,仍然尝试使用符号链接,而不是拷贝
--copies,即使符号链接是平台的默认操作,也尝试使用拷贝
--clear,在创建虚拟环境之前删除任何现有目标目录的内容
--upgrade,将虚拟环境下的python可执行文件升级到运行脚本的python版本
--without-pip,不再虚拟环境下安装或升级pip
--prompt PROMPT,将PROMRT作为此虚拟环境下的提示前缀
--upgrade-deps,升级核心依赖项(pip和setuptools)
virtualenv是一种需要python解释器才能运行的命令行工具。如果你已经有了python3.5+解释器,那么最好使用pipx将virtualenv安装到一个隔离的环境中(好处:以后您升级virtualenv的时候不会影响到系统的其他部分)
virtualenv有一个基本命令:virtualenv env,virtualenv将创建一个与其版本相同的python虚拟环境,并将虚拟环境安装到子目录env中。
virtualenv主要是一个命令行应用程序。它的主要作用是修改命令行中的环境变量以创建一个独立的python环境,因此你需要一个命令行来运行它。所有选项都有合理的默认值,并且有一个必须的参数(创建的虚拟环境的路劲)。 下面列出了可以传递给virtualenv的选项及其默认值和简短说明。
--version,显示virtualenv软件包的版本及其位置,然后退出。
--with-traceback,默认值为False,True的行为:失败会显示virtualenv内部的堆栈跟踪
--read-only-appdata,默认值为False,True的行为:以只读模式使用appdata文件夹(virtualenv用作缓存的数据文件夹)(不允许写入操作,写入操作将失败并报错)
--app-data,指定virtualenv用作缓存的数据文件夹
--reset-app-data,默认值为False,True的行为:将appdata文件夹清空
-p,--python:指定virtualenv要安装的python可执行文件。默认情况下使用安装环境的python解释器
--try-first-with:在开始查找之前,请先尝试这些解释器
--clear,默认值为False,True的行为:在启动之前删除目标目录
--system-site-packages,默认为False,True的行为:允许虚拟环境访问系统site-packages目录
--symlinks,默认为True,True的行为: 尝试使用symlinks而不是拷贝
--copies,--always-,默认False,True的行为:尝试使用副本
推荐还是使用默认的吧,除非出现错误,可以指定--with-traceback进行错误调试。
virtualenv还可以通过查找标准ini配置文件进行配置,ini配置文件具体位置取决于你使用的操作系统, 由platformdirs应用程序配置,可以查看。
虚拟环境就介绍到这里了,虚拟环境的本质就是修改shell的PATH环境变量,如果你有这方面的想法,你也可以编写一个实现虚拟环境的工具,当然,完全没有这个必要,真的,没什么意思。