标签 python 下的文章

本文是 Python 之禅特别系列的第三篇,此篇着眼于其中第五与第六条原则:扁平与稀疏。

Python 之禅 之所以得名,正是由于它那简明扼要的规则被设计出的意图在于让读者进行深入地思考,而绝不单是为编程提供一份易于遵守的指南。

读后不去三思其意,断然难以体会 Python 之禅的妙处。倘若 Python 之禅仅仅罗列出一组清晰的法则,那法则之间的矛盾是一种缺憾,然而作为引导读者沉思最优方案沉思的工具,矛盾却是绝佳的。

扁平胜过嵌套 Flat is better than nested

迫于对缩进的强硬要求,Python 对“扁平化”的需求显然远超它者。其余编程语言为了缓解对缩进的需求,通常会在嵌套结构里加入一种“作弊”的手段。为了理解这一点,不妨一同来看看 JavaScript。

JavaScript 本质上是异步的,这意味着程序员用 JavaScript 写的代码会用到大量的回调函数。

a(function(resultsFromA) {
  b(resultsFromA, function(resultsfromB) {
    c(resultsFromC, function(resultsFromC) {
      console.log(resultsFromC)
   }
  }
}

忽略这段代码的具体内容,只去观察这段代码的形状与缩进带来一个最右边的点的方式。这种独特的“箭头”图形在我们扫看代码时格外扎眼,这种写法也因此被视作不可取,甚至得到了“回调地狱”的绰号。不过,在 JavaScript 中,这种反映嵌套关系的缩进可以通过“作弊”来回避。

a(function(resultsFromA) {
b(resultsFromA,
  function(resultsfromB) {
c(resultsFromC,
  function(resultsFromC) {
    console.log(resultsFromC)
}}}

Python 并没有提供这种作弊手段:每一级嵌套在代码中都如实的对应着一层缩进。因此,Python 深层的嵌套关系在视觉上也一定是深层嵌套的。这使得“回调地狱”的问题对于 Python 而言要比在 JavaScript 中严重得多:嵌套的回调函数必定带来缩进,而绝无使用花括号来“作弊”的可能。

这项挑战与 Python 之禅的指导原则相结合后,在我参与的库中催生出了一个优雅的解决方案。我们在 Twisted 框架里提出了 deferred 抽象,日后 JavaScript 中流行的 promise 抽象亦是受其启发而生。正是由于 Python 对整洁代码的坚守,方能推动 Python 开发者去发掘新的、强力的抽象。

future_value = future_result()
future_value.addCallback(a)
future_value.addCallback(b)
future_value.addCallback(c)

(现代 JavaScript 程序员也许会觉得这段代码十分眼熟:promise 着实受到了 Twisted 里 deferred 抽象的深远影响。)

稀疏胜过密集 Sparse is better than dense

最易降低代码密集程度的方法是引入嵌套。这种习惯也正是有关稀疏的原则要随着前一条提出的原因:在竭尽所能地减少嵌套之后,我们往往会遗留下密集的代码或数据结构。此处的密集,是指塞进过量信息的小段代码,它们会导致错误发生后的解析变得困难。

这种密集性唯有通过创造性的思考方可改善,此外别无捷径。Python 之禅并不为我们提供简单的解决方案,它只会指明改善代码的方向,而非提供“如何”去做的向导。

起身走走,泡个热水澡,抑或是闻闻花香。盘坐冥思,直至灵感袭来。当你终于得到启发,便是动身写代码之时。


via: https://opensource.com/article/19/12/zen-python-flat-sparse

作者:Moshe Zadka 选题:lujun9972 译者:caiichenr 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文是 Python 之禅特别系列的第二篇,我们将要关注其中第三与第四条原则:简单与复杂。

Il semble que la perfection soit atteinte non quand il n'y a plus rien à ajouter, mais quand il n'y plus rien à retrancher.

It seems that perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away.

“完美并非无可增,而是不可减。”

—Antoine de Saint-Exupéry, Terre des Hommes, 1939

编程时最常有的考量是与复杂性的斗争,只想写出让旁人无从下手的繁杂代码,对每个程序员来讲都算不上难事。倘若未能触及代码的简繁取舍,那么 《Python 之禅》 就有了一角残缺。

简单胜过复杂 Simple is better than complex

尚有选择余地时,应该选简单的方案。Python 少有不可为之事,这意味着设计出巴洛克风格(LCTT 译注:即夸张和不理性)的程序只为解决浅显的问题不仅有可能,甚至很简单。

正因如此,编程时应当谨记,代码的简单性是最易丢失,却最难复得的。

这意味着,在可以选用函数来表达时不要去引入额外的类;避免使用强力的第三方库往往有助于你针对迫切的问题场景设计更妥当的简短函数。不过其根本的意图,旨在让你减少对将来的盘算,而去着重解决手头的问题。

以简单和优美作为指导原则的代码相比那些想要囊括将来一切变数的,在日后要容易修改得多。

复杂胜过错综复杂 Complex is better than complicated

把握用词的精确含义对于理解这条令人费解的原则是至关重要的。形容某事 复杂 complex ,是说它由许多部分组成,着重组成成分之多;而形容某事 错综复杂 complicated ,则是指其包含着差异巨大、难以预料的行为,强调的是各组成部分之间的杂乱联系。

解决困难问题时,往往没有可行的简单方案。此时,最 Python 化的策略是“ 自底向上 bottom-up ”地构建出简单的工具,之后将其组合用以解决该问题。

这正是 对象组合 object composition 这类技术的闪耀之处,它避免了错综复杂的继承体系,转而由独立的对象把一些方法调用传递给别的独立对象。这些对象都能独立地测试与部署,最终却可以组成一体。

“自底建造” 的另一例即是 单分派泛函数 singledispatch 的使用,抛弃了错综复杂的对象之后,我们得到是简单、几乎无行为的对象以及独立的行为。


via: https://opensource.com/article/19/12/zen-python-simplicity-complexity

作者:Moshe Zadka 选题:lujun9972 译者:caiichenr 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在 Linux、Mac 或 Windows 上为旧版 Python 安装 pip。

Python 是一种功能强大、流行广泛的编程语言,在常规编程、数据科学等很多方面它都有丰富的软件包可供使用。但这些软件包通常都不会在 Python 安装时自动附带,而是需要由用户自行下载、安装和管理。所有的这些软件包(包括库和框架)都存放在一个名叫 PyPI(也就是 Python 软件包索引 Python Package Index )的中央存储库当中,而 pip(也就是 首选安装程序 Preferred Installer Program )则是管理这个中央存储库的工具。

在安装 pip 之后,管理 PyPI 的软件包就变得很方便了。大部分的软件包都可以通过在终端或者命令行界面执行 python -m pip install <软件包名> 这样的命令来完成安装。

较新版本的 Python 3(3.4 或以上)和 Python 2(2.7.9 或以上)都已经预装了 pip,旧版本的 Python 没有自带 pip,但可以另外安装。

在这篇文章中,我将会介绍 pip 在 Linux、Mac 和 Windows 系统上的安装过程。想要了解更多详细信息,可以参考 pip.pypa 的文档。

首先需要安装 Python

首先你的系统中需要安装好 Python,否则 pip 安装器无法理解任何相关的命令。你可以在命令行界面、Bash 或终端执行 python 命令确认系统中是否已经安装 Python,如果系统无法识别 python 命令,请先下载 Python 并安装。安装完成后,你就可以看到一些引导你安装 pip 的提示语了。

在 Linux 上安装 pip

在不同发行版的 Linux 上,安装 pip 的命令也有所不同。

在 Fedora、RHEL 或 CentOS 上,执行以下命令安装:

$ sudo dnf install python3

在 Debian 或 Ubuntu 上,使用 apt 包管理工具安装:

$ sudo apt install python3-pip

其它的发行版可能还会有不同的包管理工具,比如 Arch Linux 使用的是 pacman

$ sudo pacman -S python-pip

执行 pip --version 命令就可以确认 pip 是否已经正确安装。

在 Linux 系统上安装 pip 就是这样简单,你可以直接跳到“使用 pip”部分继续阅读。

在 Mac 上安装 pip

MacOS 已经预装了 Python,但 Python 版本一般是比较旧的。如果你要使用 Python 的话,建议另外安装 Python 3

在 Mac 上可以使用 homebrew 安装 Python 3:

$ brew update && brew upgrade python

如果你安装的是以上提到的新版本 Python 3,它会自带 pip 工具。你可以使用以下命令验证:

$ python3 -m pip --version

如果你使用的是 Mac,安装过程也到此结束,可以从“使用 pip”部分继续阅读。

在 Windows 上安装 pip

以下 pip 安装过程是针对 Windows 8 和 Windows 10 的。下面文章中的截图是 Windows 10 环境下的截图,但对 Windows 8 同样适用。

首先确认 Python 已经安装完成

如果你想在 Windows 系统上像 Linux 一样使用包管理工具,Chocolatey 是一个不错的选择,它可以让 Python 的调用和更新变得更加方便。Chocolatey 在 PowerShell 中就可以运行,只需要简单的命令,Python 就可以顺利安装。

PS> choco install python

接下来就可以使用 pip 安装所需的软件包了。

使用 pip

在 Linux、BSD、Windows 和 Mac 上,pip 都是以同样的方式使用的。

例如安装某个库:

python3 -m pip install foo --user

卸载某个已安装的库:

python3 -m pip uninstall foo

按照名称查找软件包:

python3 -m pip search foo

pip 更新到一个新版本:

$ sudo pip install --upgrade pip

需要注意的是,在 Windows 系统中不需要在前面加上 sudo 命令,这是因为 Windows 通过独有的方式管理用户权限,对应的做法是创建一个执行策略例外

python -m pip install --upgrade pip

希望本文介绍的的方法能对你有所帮助,欢迎在评论区分享你的经验。


via: https://opensource.com/article/20/3/pip-linux-mac-windows

作者:Vijay Singh Khatri 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你对如何让调试器变得更快产生过兴趣吗?本文将分享我们在为 Python 构建调试器时得到的一些经验。

整段故事讲的是我们在 Rookout 公司的团队为 Python 调试器开发不中断断点的经历,以及开发过程中得到的经验。我将在本月于旧金山举办的 PyBay 2019 上介绍有关 Python 调试过程的更多细节,但现在就让我们立刻开始这段故事。

Python 调试器的心脏:sys.set\_trace

在诸多可选的 Python 调试器中,使用最广泛的三个是:

  • pdb,它是 Python 标准库的一部分
  • PyDev,它是内嵌在 Eclipse 和 Pycharm 等 IDE 中的调试器
  • ipdb,它是 IPython 的调试器

Python 调试器的选择虽多,但它们几乎都基于同一个函数:sys.settrace。 值得一提的是, sys.settrace 可能也是 Python 标准库中最复杂的函数。

 title=

简单来讲,settrace 的作用是为解释器注册一个跟踪函数,它在下列四种情形发生时被调用:

  • 函数调用
  • 语句执行
  • 函数返回
  • 异常抛出

一个简单的跟踪函数看上去大概是这样:

def simple_tracer(frame, event, arg):
  co = frame.f_code
  func_name = co.co_name
  line_no = frame.f_lineno
  print("{e} {f} {l}".format(
e=event, f=func_name, l=line_no))
  return simple_tracer

在分析函数时我们首先关注的是参数和返回值,该跟踪函数的参数分别是:

  • frame,当前堆栈帧,它是包含当前函数执行时解释器里完整状态的对象
  • event,事件,它是一个值可能为 calllinereturnexception 的字符串
  • arg,参数,它的取值基于 event 的类型,是一个可选项

该跟踪函数的返回值是它自身,这是由于解释器需要持续跟踪两类跟踪函数:

  • 全局跟踪函数(每线程):该跟踪函数由当前线程调用 sys.settrace 来设置,并在解释器创建一个新的堆栈帧时被调用(即代码中发生函数调用时)。虽然没有现成的方式来为不同的线程设置跟踪函数,但你可以调用 threading.settrace 来为所有新创建的 threading 模块线程设置跟踪函数。
  • 局部跟踪函数(每一帧):解释器将该跟踪函数的值设置为全局跟踪函数创建帧时的返回值。同样也没有现成的方法能够在帧被创建时自动设置局部跟踪函数。

该机制的目的是让调试器对被跟踪的帧有更精确的把握,以减少对性能的影响。

简单三步构建调试器 (我们最初的设想)

仅仅依靠上文提到的内容,用自制的跟踪函数来构建一个真正的调试器似乎有些不切实际。幸运的是,Python 的标准调试器 pdb 是基于 Bdb 构建的,后者是 Python 标准库中专门用于构建调试器的基类。

基于 Bdb 的简易断点调试器看上去是这样的:

import bdb
import inspect

class Debugger(bdb.Bdb):
  def __init__(self):
      Bdb.__init__(self)
      self.breakpoints = dict()
      self.set_trace()

def set_breakpoint(self, filename, lineno, method):
  self.set_break(filename, lineno)
  try :
      self.breakpoints[(filename, lineno)].add(method)
  except KeyError:
      self.breakpoints[(filename, lineno)] = [method]

def user_line(self, frame):
  if not self.break_here(frame):
      return

  # Get filename and lineno from frame
  (filename, lineno, _, _, _) = inspect.getframeinfo(frame)

  methods = self.breakpoints[(filename, lineno)]
  for method in methods:
      method(frame)

这个调试器类的全部构成是:

  1. 继承 Bdb,定义一个简单的构造函数来初始化基类,并开始跟踪。
  2. 添加 set_breakpoint 方法,它使用 Bdb 来设置断点,并跟踪这些断点。
  3. 重载 Bdb 在当前用户行调用的 user_line 方法,该方法一定被一个断点调用,之后获取该断点的源位置,并调用已注册的断点。

这个简易的 Bdb 调试器效率如何呢?

Rookout 的目标是在生产级性能的使用场景下提供接近普通调试器的使用体验。那么,让我们来看看先前构建出来的简易调试器表现的如何。

为了衡量调试器的整体性能开销,我们使用如下两个简单的函数来进行测试,它们分别在不同的情景下执行了 1600 万次。请注意,在所有情景下断点都不会被执行。

def empty_method():
   pass

def simple_method():
   a = 1
   b = 2
   c = 3
   d = 4
   e = 5
   f = 6
   g = 7
   h = 8
   i = 9
   j = 10

在使用调试器的情况下需要大量的时间才能完成测试。糟糕的结果指明了,这个简陋 Bdb 调试器的性能还远不足以在生产环境中使用。

 title=

对调试器进行优化

降低调试器的额外开销主要有三种方法:

  1. 尽可能的限制局部跟踪:由于每一行代码都可能包含大量事件,局部跟踪比全局跟踪的开销要大得多。
  2. 优化 call 事件并尽快将控制权还给解释器:在 call 事件发生时调试器的主要工作是判断是否需要对该事件进行跟踪。
  3. 优化 line 事件并尽快将控制权还给解释器:在 line 事件发生时调试器的主要工作是判断我们在此处是否需要设置一个断点。

于是我们复刻了 Bdb 项目,精简特征、简化代码,针对使用场景进行优化。这些工作虽然得到了一些效果,但仍无法满足我们的需求。因此我们又继续进行了其它的尝试,将代码优化并迁移至 .pyx 使用 Cython 进行编译,可惜结果(如下图所示)依旧不够理想。最终,我们在深入了解 CPython 源码之后意识到,让跟踪过程快到满足生产需求是不可能的。

 title=

放弃 Bdb 转而尝试字节码操作

熬过先前对标准调试方法进行的试验-失败-再试验循环所带来的失望,我们将目光转向另一种选择:字节码操作。

Python 解释器的工作主要分为两个阶段:

  1. 将 Python 源码编译成 Python 字节码:这种(对人类而言)不可读的格式专为执行的效率而优化,它们通常缓存在我们熟知的 .pyc 文件当中。
  2. 遍历 解释器循环中的字节码: 在这一步中解释器会逐条的执行指令。

我们选择的模式是:使用字节码操作来设置没有全局额外开销的不中断断点。这种方式的实现首先需要在内存中的字节码里找到我们感兴趣的部分,然后在该部分的相关机器指令前插入一个函数调用。如此一来,解释器无需任何额外的工作即可实现我们的不中断断点。

这种方法并不依靠魔法来实现,让我们简要地举个例子。

首先定义一个简单的函数:

def multiply(a, b):
   result = a * b
   return result

inspect 模块(其包含了许多实用的单元)的文档里,我们得知可以通过访问 multiply.func_code.co_code 来获取函数的字节码:

'|\x00\x00|\x01\x00\x14}\x02\x00|\x02\x00S'

使用 Python 标准库中的 dis 模块可以翻译这些不可读的字符串。调用 dis.dis(multiply.func_code.co_code) 之后,我们就可以得到:

  4          0 LOAD_FAST               0 (a)
             3 LOAD_FAST               1 (b)
             6 BINARY_MULTIPLY    
             7 STORE_FAST              2 (result)

  5         10 LOAD_FAST               2 (result)
            13 RETURN_VALUE      

与直截了当的解决方案相比,这种方法让我们更靠近发生在调试器背后的事情。可惜 Python 并没有提供在解释器中修改函数字节码的方法。我们可以对函数对象进行重写,不过那样做的效率满足不了大多数实际的调试场景。最后我们不得不采用一种迂回的方式来使用原生拓展才能完成这一任务。

总结

在构建一个新工具时,总会学到许多事情的工作原理。这种刨根问底的过程能够使你的思路跳出桎梏,从而得到意料之外的解决方案。

在 Rookout 团队中构建不中断断点的这段时间里,我学到了许多有关编译器、调试器、服务器框架、并发模型等等领域的知识。如果你希望更深入的了解字节码操作,谷歌的开源项目 cloud-debug-python 为编辑字节码提供了一些工具。


via: https://opensource.com/article/19/8/debug-python

作者:Liran Haimovitch 选题:lujun9972 译者:caiichenr 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

入狱后,开源编程是如何提供机会的。

不到一年前,我还在圣昆廷州立监狱服刑,我是无期徒刑。

我高三的时候,我抢劫了一个人并向他开了枪。现在,我经过一段时间才意识到并承认自己做错了,这是在经历了陪审团审判并看到我的行为带来的恶果后,我知道需要改变自己,我也确实做到了。尽管我对我的行为表示懊悔,但我毕竟开枪打了一个人,并差点杀了他。做这样的事是有后果的,这是理所当然的。所以在我 18 岁的时候,我被判了终身监禁。

监狱是一个非常可怕的地方;我是不推荐你去的。但是我必须去,所以我去了。我不告诉你具体的细节,但你可以放心,这是一个没有太多动机去改变的地方,许多人在这里养成的坏习惯比他们过去在别处养成的更多。

我是幸运儿之一。当我在服刑的时候,发生了一些不同寻常的事情。我开始想象自己出狱后的的未来,虽然在这之前,我还是已经在那里度过了我整个成年生活。

现在你想想:我是黑人,只受过高中教育。我没有工作经历,如果我离开监狱,在被释放前,我还是一个被定罪的重罪犯。当每个雇主看到我的简历,都不会有“我需要雇用这个人”想法,我认为是正常的。

我不知道我的选择是什么,但我已经下定决心了。我需要做些活下去的事情,并且这和我入狱前的生活一点也不像。

Python 之路

最终,我被关在了圣昆廷州立监狱,我不知道我为何幸运地被关在那里。圣昆廷提供了几个自助和教育编程项目。这些改造机会帮助囚犯使他们拥有在获释后避免再次犯罪的技能。

作为其中一个编程项目的一部分,2017 年我通过圣昆廷媒体项目认识了杰西卡·麦凯拉。杰西卡是编程语言 Python 的爱好者,她开始向我推荐 Python 有多棒,以及它是刚起步的人学习的完美语言。这就是故事变得比小说更精彩的地方。

感谢 @northbaypython@ShaStepter 和我重复 @pycon 的主题演讲,让他们被录制下来。我很荣幸与大家分享:

从监狱到 Pythone: https://t.co/rcumoAgZHm

大规模裁员:如果我们不雇佣被判重罪的人,谁会呢? https://t.co/fENDUFdxfX

pic.Twitter.com/kpjo8d3ul6

—杰西卡·麦凯拉(@jessicamckellar)2019 年 11 月 5 日

杰西卡向我介绍了一些 Python 视频教程,这些教程是她为一家名叫 O’Reilly Media 的公司做的,课程是在线的,如果我能接触到它们,那该有多好呀。不幸的是,在监狱里上网是不可能的。但是,我遇到了一个叫 Tim O’Reilly 的人,他最近刚来到圣昆廷。在他访问之后,Tim 从他的公司 O’Reilly Media 公司向监狱的编程班捐赠了大量内容。最终,我拿到了一款平板电脑,上面有杰西卡的 Python 教程,并学会了如何使用这些 Python 教程进行编码。

真是难以置信。背景和生活与我完全不同的陌生人把这些联系在一起,让我学会了编码。

对 Python 社区的热爱

在这之后,我开始经常和杰西卡见面,她开始告诉我关于开源社区的情况。我了解到,从根本上说,开源社区就是关于伙伴关系和协作的社区。之所以如此有效,是因为没有人被排除在外。

对我来说,一个努力寻找自己定位的人,我所看到的是一种非常基本的爱——通过合作和接受的爱,通过接触的爱,通过包容的爱。我渴望成为其中的一部分。所以我继续学习 Python,不幸的是,我无法获得更多的教程,但是我能够从开源社区收集的大量书面知识中获益。我读一切提到 Python 的东西,从平装本到晦涩难懂的杂志文章,我使用平板电脑来解决我读到的 Python 问题。

我对 Python 和编程的热情不是我的许多同龄人所共有的。除了监狱编程课上的极少数人之外,我认识的其他人都没有提到过编程;一般囚犯都不知道。我认为这是因为有过监禁经历的人无法接触编程,尤其是如果你是有色人种。

监狱外的 Python 生活

然而,在 2018 年 8 月 17 日,我得到了生命中的惊喜。时任州长的杰里·布朗将我 27 年的刑期减刑,在服刑将近 19 年后,我被释放出狱了。

但现实情况是,这也是为什么我认为编程和开源社区如此有价值。我是一名 37 岁的黑人罪犯,没有工作经历,刚刚在监狱服刑 18 年。我有犯罪史,并且现存偏见导致没有多少职业适合我。但是编程是少数例外之一。

现在,监禁后重返社会的人们迫切需要包容,但当谈及工作场所的多样性以及对多样性的需求时,你真的听不到这个群体被提及或包容。

还有什么:

1、背景调查:询问他们在你的公司是如何使用的。

2、初级角色:删除虚假的、不必要的先决条件,这些条件将排除有记录的合格人员。

3、积极拓展:与当地再就业项目合作,创建招聘渠道。

pic.twitter.com/WnzdEUTuxr

—杰西卡·麦凯拉(@jessicamckellar)2019 年 5 月 12 日

因此,我想谦卑地挑战开源社区的所有程序员和成员,让他们围绕包容和多样性展开思考。今天,我自豪地站在你们面前,代表一个大多数人都没有想到的群体——以前被监禁的人。但是我们存在,我们渴望证明我们的价值,最重要的是,我们期待被接受。当我们重返社会时,许多挑战等待着我们,我请求你们允许我们有机会展示我们的价值。欢迎我们,接受我们,最重要的是,包容我们。


via: https://opensource.com/article/20/1/prison-to-python

作者:Shadeed Sha Wallace-Stepter 选题:lujun9972 译者:heguangzhi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

了解一下 setV,它是一个轻量级的 Python 虚拟环境管理器,是 virtualenvwrapper 的替代产品。

这一年多来,我的 bash\_scripts 项目中悄悄隐藏这 setV,但现在是时候该公开了。setV 是一个 Bash 函数,我可以用它代替 virtualenvwrapper。它提供了使你能够执行以下操作的基本功能:

  • 默认使用 Python 3
  • 创建一个新的虚拟环境
  • 使用带有 -p(或 --python)的自定义 Python 路径来创建新的虚拟环境
  • 删除现有的虚拟环境
  • 列出所有现有的虚拟环境
  • 使用制表符补全(以防你忘记虚拟环境名称)

安装

要安装 setV,请下载该脚本:

curl https://gitlab.com/psachin/setV/raw/master/install.sh

审核一下脚本,然后运行它:

sh ./install.sh

当安装 setV 时,安装脚本会要求你引入(source)一下 ~/.bashrc~/.bash_profile 的配置,根据你的喜好选择一个。

用法

基本的命令格式是 setv

创建虚拟环境

setv --new rango  # setv -n rango

# 或使用定制的 Python 路径
setv --new --python /opt/python/python3 rango  # setv -n -p /opt/python/python3 rango

激活已有的虚拟环境

setv VIRTUAL_ENVIRONMENT_NAME
# 示例
setv rango

列出所有的虚拟环境

setv --list
# 或
setv [TAB] [TAB]

删除虚拟环境

setv --delete rango

切换到另外一个虚拟环境

# 假设你现在在 'rango',切换到 'tango'
setv tango

制表符补完

如果你不完全记得虚拟环境的名称,则 Bash 式的制表符补全也可以适用于虚拟环境名称。

参与其中

setV 在 GNU GPLv3下开源,欢迎贡献。要了解更多信息,请访问它的 GitLab 存储库中的 setV 的 README 的贡献部分。

setV 脚本

#!/usr/bin/env bash
# setV - A Lightweight Python virtual environment manager.
# Author: Sachin (psachin) <[email protected]>
# Author's URL: https://psachin.gitlab.io/about
#
# License: GNU GPL v3, See LICENSE file
#
# Configure(Optional):
# Set `SETV_VIRTUAL_DIR_PATH` value to your virtual environments
# directory-path. By default it is set to '~/virtualenvs/'
#
# Usage:
# Manual install: Added below line to your .bashrc or any local rc script():
# ---
# source /path/to/virtual.sh
# ---
#
# Now you can 'activate' the virtual environment by typing
# $ setv <YOUR VIRTUAL ENVIRONMENT NAME>
#
# For example:
# $ setv rango
#
# or type:
# setv [TAB] [TAB]  (to list all virtual envs)
#
# To list all your virtual environments:
# $ setv --list
#
# To create new virtual environment:
# $ setv --new new_virtualenv_name
#
# To delete existing virtual environment:
# $ setv --delete existing_virtualenv_name
#
# To deactivate, type:
# $ deactivate

# Path to virtual environment directory
SETV_VIRTUAL_DIR_PATH="$HOME/virtualenvs/"
# Default python version to use. This decides whether to use `virtualenv` or `python3 -m venv`
SETV_PYTHON_VERSION=3  # Defaults to Python3
SETV_PY_PATH=$(which python${SETV_PYTHON_VERSION})

function _setvcomplete_()
{
    # Bash-autocompletion.
    # This ensures Tab-auto-completions work for virtual environment names.
    local cmd="${1##*/}" # to handle command(s).
                         # Not necessary as such. 'setv' is the only command

    local word=${COMP_WORDS[COMP_CWORD]} # Words thats being completed
    local xpat='${word}'                 # Filter pattern. Include
                                         # only words in variable '$names'
    local names=$(ls -l "${SETV_VIRTUAL_DIR_PATH}" | egrep '^d' | awk -F " " '{print $NF}') # Virtual environment names

    COMPREPLY=($(compgen -W "$names" -X "$xpat" -- "$word")) # compgen generates the results
}

function _setv_help_() {
    # Echo help/usage message
    echo "Usage: setv [OPTIONS] [NAME]"
    echo Positional argument:
    echo -e "NAME                       Activate virtual env."
    echo Optional arguments:
    echo -e "-l, --list                 List all Virtual Envs."
    echo -e "-n, --new NAME             Create a new Python Virtual Env."
    echo -e "-d, --delete NAME          Delete existing Python Virtual Env."
    echo -e "-p, --python PATH          Python binary path."
}

function _setv_custom_python_path()
{
    if [ -f "${1}" ];
    then
        if [ "`expr $1 : '.*python\([2,3]\)'`" = "3" ];
        then
            SETV_PYTHON_VERSION=3
        else
            SETV_PYTHON_VERSION=2
        fi
        SETV_PY_PATH=${1}
        _setv_create $2
    else
        echo "Error: Path ${1} does not exist!"
    fi
}

function _setv_create()
{
    # Creates new virtual environment if ran with -n|--new flag
    if [ -z ${1} ];
    then
        echo "You need to pass virtual environment name"
        _setv_help_
    else
        echo "Creating new virtual environment with the name: $1"

        if [ ${SETV_PYTHON_VERSION} -eq 3 ];
        then
            ${SETV_PY_PATH} -m venv ${SETV_VIRTUAL_DIR_PATH}${1}
        else
            virtualenv -p ${SETV_PY_PATH} ${SETV_VIRTUAL_DIR_PATH}${1}
        fi

        echo "You can now activate the Python virtual environment by typing: setv ${1}"
    fi
}

function _setv_delete()
{
    # Deletes virtual environment if ran with -d|--delete flag
    # TODO: Refactor
    if [ -z ${1} ];
    then
        echo "You need to pass virtual environment name"
        _setv_help_
    else
        if [ -d ${SETV_VIRTUAL_DIR_PATH}${1} ];
        then
            read -p "Really delete this virtual environment(Y/N)? " yes_no
            case $yes_no in
                Y|y) rm -rvf ${SETV_VIRTUAL_DIR_PATH}${1};;
                N|n) echo "Leaving the virtual environment as it is.";;
                *) echo "You need to enter either Y/y or N/n"
            esac
        else
            echo "Error: No virtual environment found by the name: ${1}"
        fi
    fi
}

function _setv_list()
{
    # Lists all virtual environments if ran with -l|--list flag
    echo -e "List of virtual environments you have under ${SETV_VIRTUAL_DIR_PATH}:\n"
    for virt in $(ls -l "${SETV_VIRTUAL_DIR_PATH}" | egrep '^d' | awk -F " " '{print $NF}')
    do
        echo ${virt}
    done
}

function setv() {
    # Main function
    if [ $# -eq 0 ];
    then
        _setv_help_
    elif [ $# -le 3 ];
    then
        case "${1}" in
            -n|--new) _setv_create ${2};;
            -d|--delete) _setv_delete ${2};;
            -l|--list) _setv_list;;
            *) if [ -d ${SETV_VIRTUAL_DIR_PATH}${1} ];
               then
                   # Activate the virtual environment
                   source ${SETV_VIRTUAL_DIR_PATH}${1}/bin/activate
               else
                   # Else throw an error message
                   echo "Sorry, you don't have any virtual environment with the name: ${1}"
                   _setv_help_
               fi
               ;;
        esac
    elif [ $# -le 5 ];
    then
        case "${2}" in
            -p|--python) _setv_custom_python_path ${3} ${4};;
            *) _setv_help_;;
        esac
    fi
}

# Calls bash-complete. The compgen command accepts most of the same
# options that complete does but it generates results rather than just
# storing the rules for future use.
complete  -F _setvcomplete_ setv

via: https://opensource.com/article/20/1/setv-bash-function

作者:Sachin Patil 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出