python - Poetry介绍
一、简介
Poetry 是一个Python 中的好用的包管理工具。在 Python 中,打包系统和依赖管理非常复杂:一个项目经常要同时创建多个文件,例如:
setuppy
requirementstxt
setupcfg
MANIFESTin
Pipfile
基于此, poetry 将所有的配置都放置在一个 toml 文件中,包括:依赖管理、构建、打包、发布等,可谓是简单方便。
二、安装
Poetry 要求 Python 版本为 27 或者 35+。Poetry 官方提供了一个脚本,可以快速方便地进行安装。
osx / linux / bashonwindows 安装:
curl -sSL https://rawgithubusercontentcom/python-poetry/poetry/master/install-poetrypy | python -
windows powershell 安装:
(Invoke-WebRequest -Uri https://rawgithubusercontentcom/python-poetry/poetry/master/install-poetrypy -UseBasicParsing)Content | python -
Poetry 会被安装在系统中的如下位置:
$HOME/local/bin Unix系统
%APPDATA%\Python\Scripts Windows系统
然后把路径添加到系统变量 PATH 中,即可使用 poetry 命令调用:
poetry --version
卸载:
python install-poetrypy --uninstall
POETRY_UNINSTALL=1 python install-poetrypy
如果你想要改变安装的默认路径,可以设置 POETRY_HOME :
POETRY_HOME=/etc/poetry python install-poetrypy
除了官方的安装脚本,也可以使用 pipx 或者 pip 进行安装:
pipx install poetry
pipx upgrade poetry
pipx uninstall poetry
pip install --user poetry
更新:
poetry self update
三、基础使用
在已有项目中执行:
poetry init
该命令创建了一个pyprojecttoml 文件。你可以手动修改 pyprojecttoml 文件添加依赖,然后运行:
poetry install
也可以执行 add 命令安装具体某个模块并自动添加到 pyprojecttoml:
$ poetry add xxxx
默认情况下,poetry会在 {cache-dir}/virtualenvs 下创建虚拟环境,你也可以手动修改该配置项,或者在 pyprojecttoml 配置[virtualenvsin-project] 在你的项目目录中创建虚拟环境。
你可以使用 run 命令在虚拟环境中运行脚本:
poetry run python your_scriptpy
或者直接激活你的虚拟环境,新建一个 shell 运行:
poetry shell
只安装dependencies :
poetry install --no-root
更新所有锁定版本的依赖:
poetry update
四、命令选项
全局选项:
--verbose (-v|vv|vvv): "-v" 正常输出, "-vv" 详细输出 "-vvv" debug
--help (-h) : 帮助信息
--quiet (-q) : 不输出任何信息
--ansi: 强制 ANSI 输出
--no-ansi: 禁止ANSI 输出
--version (-V): 显示版本
--no-interaction (-n): 禁止交互询问
NEW:
poetry new my-package
创建项目模板,项目结构如下所示:
my-package
├── pyprojecttoml
├── READMEmd
├── my_package
│ └── init py
└── tests
└── init py
init:创建pyprojecttoml文件 。
install:读取pyprojecttoml并安装依赖,它具有如下这些选项:
--without: 忽略依赖
--with: 安装可选的依赖
--only: 只安装指定的依赖
--default: 只安装默认的依赖
--sync: 同步锁定的版本至环境中
--no-root: 不安装根依赖包
--dry-run: 输出操作但不执行
--extras (-E): 安装额外的包
update:升级包
poetry update
不指定任何包时,更新所有,也可以指定升级包:
poetry update requests toml
它具有如下选项:
--dry-run : 输出操作但不执行
--no-dev : 不按照开发依赖
--lock : 只更新锁定不安装
add: 添加依赖并安装
限制范围:
poetry add pendulum@^205
poetry add "pendulum>=205"
它具有如下选项:
--group (-D): 分组
--editable (-e): 添加到编辑模式
--extras (-E): 添加额外的依赖
--optional: 添加至可选依赖
--python: 指定python版本
--platform: 指定操作系统
--source: 使用源名称安装
---allow-prereleases: 接受 prereleases 安装
--dry-run: 输出操作但不执行
--lock: 只更新锁定不安装
remove:移除依赖
它具有如下选项:
--group (-D): 分组
--dry-run : 输出操作但不执行
show:列出所有的可安装的包
如果你想看具体某个包的信息:
poetry show pendulum
name : pendulum
version : 142
description : Python datetimes made easy
dependencies:
--without: 忽略依赖
--with: 同时显示
--only: 只显示指定的依赖
--default: 只显示默认的
--no-dev: 不显示开发的依赖
--tree: 以树状形式显示
--latest (-l): 展示最新的版本
--outdated (-o): 显示最新版本,但仅适用于过时的软件包
build:构建
publish:发布
config:配置项
使用方法:
poetry config [options] [setting-key] [setting-value1] [setting-valueN]
它具有如下选项:
--unset: 删除配置项
--list: 展示现在的配置
run:在虚拟环境中执行命令
shell:激活虚拟环境
check:检查pyprojecttoml文件
search:搜索远程包
lock:锁定版本
version:显示版本
export:导出锁定的文件为其他的格式
poetry export -f requirementstxt --output requirementstxt
它具有如下选项:
--format (-f): 转换的格式,暂时只支持requirementstxt
--output (-o): 输出文件名字
--dev: 包括开发的依赖
--extras (-E): 额外的依赖
--without-hashes: 忽略哈希
--with-credentials: 包括合格证书
env:与虚拟环境进行交互
cache:缓存
显示缓存列表:
poetry cache list
清除缓存:
poetry cache clear pypi --all
plugin:插件
安装插件:
poetry plugin add poetry-plugin
显示插件列表:
poetry plugin show
移除插件:
poetry plugin remove poetry-plugin
source: 仓库源
添加源:
poetry source add pypi-test https://testpypiorg/simple/
显示仓库源列表:
poetry source show
移除:
poetry source remove pypi-test
五、配置
你可以运行config命令进行配置,或者直接修改configtoml文件,这个文件通常位于:
macOS: ~/Library/Application Support/pypoetry
Windows: C:\Users<username>\AppData\Roaming\pypoetry
Unix~/config/pypoetry
可以使用--local命令对具体项目进行配置:
poetry config virtualenvscreate false --local
配置项:
cache-dir缓存目录
installerparallel并行安装
virtualenvscreate如果不存在,则新建一个虚拟环境
virtualenvsin-project在项目根目录创建虚拟环境
virtualenvspath虚拟环境路径
virtualenvsoptionsalways-copy复制源文件还是创建链接到虚拟环境
virtualenvsoptionssystem-site-packages虚拟环境获得系统包的权限
repositories<name>设置一个新的可选仓库
六、依赖配置
依赖的配置有很多种写法:
版本限制:
尖括号:^12 代表 >=120 <200
波浪号:~123 代表 >=123 <130
星号:1 代表 >=100 <200
使用git仓库:
[toolpoetrydependencies]
requests = { git = " https://githubcom/requests/requestsgit " }
使用本地路径:
[toolpoetrydependencies]
my-package = { path = "/my-package/", develop = false }
my-package = { path = "/my-package/dist/my-package-010targz" }
使用URL:
[toolpoetrydependencies]
my-package = { url = " https://examplecom/my-package-010targz " }
python限制:
[toolpoetrydependencies]
pathlib2 = { version = "^22", python = "~27" }
环境限制:
[toolpoetrydependencies]
pathlib2 = { version = "^22", markers = "python_version ~= '27' or sys_platform == 'win32'" }
组合:
[toolpoetrydependencies]
foo = [
{version = "<=19", python = "^27"},
{version = "^20", python = "^34"}
]
如果限制很多,写成一行不方便阅读,可以写成多行:
[toolpoetrygroupdevdependencies]
black = {version = "1910b0", allow-prereleases = true, python = "^36", markers = "platform_python_implementation == 'CPython'"}
写成多行后:
[toolpoetrygroupdevdependenciesblack]
version = "1910b0"
allow-prereleases = true
python = "^36"
markers = "platform_python_implementation == 'CPython'"
分组功能:
[toolpoetrygrouptestdependencies]
pytest = "^600"
pytest-mock = ""
例如以上,就建立了一个test的组合的依赖。
下面这两种写法是等价的:
[toolpoetrydev-dependencies]
pytest = "^600"
pytest-mock = ""
或者:
[toolpoetrygroupdevdependencies]
pytest = "^600"
pytest-mock = ""
以上两种写法都声明了一个dev的组的依赖。
声明组合是可选的,这在具体的环境中有的特定的用途时很有用:
[toolpoetrygroupdocs]
optional = true
[toolpoetrygroupdocsdependencies]
mkdocs = ""
添加依赖到组中:
poetry add pytest --group test
同步依赖,只使用poetrylock中的依赖,移除其他不是必须的依赖:
poetry install --sync
七、环境管理
Poetry可以为项目使用独立的虚拟环境,而不是使用系统安装的。
切换环境:
poetry env use /full/path/to/python
poetry env use python37
poetry env use system
显示当前激活的环境信息:
poetry env info
运行命令会输出如下信息:
Virtual environment
Python: 371
Implementation: CPython
Path: /path/to/poetry/cache/virtualenvs/test-O3eWbxRl-py37
Valid: True
System
Platform: darwin
OS: posix
Python: /path/to/main/python
列出所有的虚拟环境列表:
poetry env list
删除环境:
poetry env remove /full/path/to/python
poetry env remove python37
poetry env remove 37
poetry env remove test-O3eWbxRl-py37
1 缘起
本文试着向读者们介绍自然语言处理(Natural Language Processing)这一领域,通常简称为 NLP。然而,不同于一般只是描述 NLP 重要概念的文章,本文还借助 Python 来形象地说明。对于不熟悉 Python 的读者们,本文也提供了部分参考资料教你如何进行 Python 编程。
2 相关介绍
21 自然语言处理
自然语言处理广纳了众多技术,对自然或人类语言进行自动生成,处理与分析。虽然大部分 NLP 技术继承自语言学和人工智能,但同样受到诸如机器学习,计算统计学和认知科学这些相对新兴的学科影响。
在展示 NLP 技术的例子前,有必要介绍些非常基础的术语。请注意:为了让文章通俗易懂,这些定义在语言上就不一定考究。
词例(Token):对输入文本做任何实际处理前,都需要将其分割成诸如词、标点符号、数字或纯字母数字(alphanumerics)等语言单元(linguistic units)。这些单元被称为词例。
句子:由有序的词例序列组成。
词例还原(Tokenization):将句子还原成所组成的词例。以分割型语言(segmented languages)英语为例,空格的存在使词例还原变得相对容易同时也索然无味。然而,对于汉语和阿拉伯语,因为没有清晰的边界,这项工作就稍显困难。另外,在某些非分割型语言(non-segmented languages)中,几乎所有的字符(characters)都能以单字(one-character)存在,但同样也可以组合在一起形成多字(multi-characterwords)形式。
语料库:通常是由丰富句子组成的海量文本。
词性标签(Part-of-speech (POS) Tag):任一单词都能被归入到至少一类词汇集(set of lexical)或词性条目(part-of-speech categories)中,例如:名词、动词、形容词和冠词等。词性标签用符号来代表一种词汇条目——NN(名词)、VB(动词)、JJ(形容词)和 AT(冠词)。Brown Corpus 是最悠久,也是最常用的标注集之一。详情且听下回分解。
剖析树(Parse Tree):利用形式语法(formal grammar)的定义,可以用树状图来表示给定句子的句法(syntactic)结构。
认识了基本的术语,下面让我们了解 NLP 常见的任务:
词性标注(POS Tagging):给定一个句子和组词性标签,常见的语言处理就是对句子中的每个词进行标注。举个例子,The ball is red,词性标注后将变成 The/AT ball/NN is/VB red/JJ。最先进的词性标注器[9]准确率高达 96%。文本的词性标注对于更复杂的 NLP 问题,例如我们后面会讨论到的句法分析(parsing)和机器翻译(machine translation)非常必要。
计算形态学(Computational Morphology):大量建立在“语素”(morphemes/stems)基础上的词组成了自然语言,语素虽然是最小的语言单元,却富含意义。计算形态学所关心的是用计算机发掘和分析词的内部结构。
句法分析(Parsing):在语法分析的问题中,句法分析器(parser)将给定句子构造成剖析树。为了分析语法,某些分析器假定一系列语法规则存在,但目前的解析器已经足够机智地借助复杂的统计模型[1]直接推断分析树。多数分析器能够在监督式设置(supervised setting)下操作并且句子已经被词性标注过了。统计句法分析是自然语言处理中非常活跃的研究领域。
机器翻译(Machine Translation(MT)):机器翻译的目的是让计算机在没有人工干预的情况下,将给定某种语言的文本流畅地翻译成另一种语言文本。这是自然语言处理中最艰巨的任务之一,这些年来已经用许多不同的方式解决。几乎所有的机器翻译方法都依赖了词性标注和句法分析作为预处理。
22 Python
Python 是一种动态类型(dynamically-typed),面向对象的解释式(interpreted)编程语言。虽然它的主要优势在于允许编程人员快速开发项目,但是大量的标准库使它依然能适应大规模产品级工程项目。Python 的学习曲线非常陡峭并且有许多优秀的在线学习资源[11]。
23 自然语言工具集(Natural Language Toolkit)
尽管 Python 绝大部分的功能能够解决简单的 NLP 任务,但不足以处理标准的自然语言处理任务。这就是 NLTK (自然语言处理工具集)诞生的原因。NLTK 集成了模块和语料,以开源许可发布,允许学生对自然语言处理研究学习和生产研究。使用 NLTK 最大的优势是集成化(entirely self-contained),不仅提供了方便的函数和封装用于建立常见自然语言处理任务块,而且提供原始和预处理的标准语料库版本应用在自然语言处理的文献和课程中。
3 使用 NLTK
NLTK 官网提供了很棒的说明文件和教程进行学习指导[13]。单纯复述那些作者们的文字对于他们和本文都不公平。因此我会通过处理四个难度系数依次上升的 NLP 任务来介绍 NLTK。这些任务都来自于 NLTK 教程中没有给出答案的练习或者变化过。所以每个任务的解决办法和分析都是本文原创的。
31 NLTK 语料库
正如前文所说,NLTK 囊括数个在 NLP 研究圈里广泛使用的实用语料库。在本节中,我们来看看三个下文会用到的语料库:
布朗语料库(Brown Corpus):Brown Corpus of Standard American English 被认为是第一个可以在计算语言学处理[6]中使用的通用英语语料库。它包含了一百万字 1961 年出版的美语文本。它代表了通用英语的样本,采样自小说,新闻和宗教文本。随后,在大量的人工标注后,诞生了词性标注过的版本。
古登堡语料库(Gutenberg Corpus):古登堡语料库从最大的在线免费电子书[5]平台 古登堡计划(Gutenberg Project) 中选择了 14 个文本,整个语料库包含了一百七十万字。
Stopwords Corpus:除了常规的文本文字,另一类诸如介词,补语,限定词等含有重要的语法功能,自身却没有什么含义的词被称为停用词(stop words)。NLTK 所收集的停用词语料库(Stopwords Corpus)包含了 来自 11 种不同语言(包括英语)的 2400 个停用词。
32 NLTK 命名约定
在开始利用 NLTK 处理我们的任务以前,我们先来熟悉一下它的命名约定(naming conventions)。最顶层的包(package)是 nltk,我们通过使用完全限定(fully qualified)的加点名称例如:nltkcorpus and nltkutilities 来引用它的内置模块。任何模块都能利用 Python 的标准结构 from import 来导入顶层的命名空间。
33 任务 1 : 探索语料库
上文提到,NLTK 含有多个 NLP 语料库。我们把这个任务制定为探索其中某个语料库。
任务:用 NLTK 的 corpus 模块读取包含在古登堡语料库的 austen-persuasiontxt,回答以下问题:
这个语料库一共有多少字?
这个语料库有多少个唯一单词(unique words)?
前 10 个频率最高的词出现了几次?
利用 corpus 模块可以探索内置的语料库,而且 NLTK 还提供了包含多个好用的类和函数在概率模块中,可以用来计算任务中的概率分布。其中一个是 FreqDist,它可以跟踪分布中的采样频率(sample frequencies)。清单1 演示了如何使用这两个模块来处理第一个任务。
清单 1: NLTK 内置语料库的探索
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 导入 gutenberg 集
>>> from nltkcorpus import gutenberg
# 都有些什么语料在这个集合里?
>>> print gutenbergfileids()
['austen-emmatxt', 'austen-persuasiontxt', 'austen-sensetxt', 'bible-kjvtxt', 'blake-poemstxt', 'bryant-storiestxt', 'burgess-busterbrowntxt', 'carroll-alicetxt', 'chesterton-balltxt', 'chesterton-browntxt', 'chesterton-thursdaytxt', 'edgeworth-parentstxt', 'melville-moby_dicktxt', 'milton-paradisetxt', 'shakespeare-caesartxt', 'shakespeare-hamlettxt', 'shakespeare-macbethtxt', 'whitman-leavestxt']
# 导入 FreqDist 类
>>> from nltk import FreqDist
# 频率分布实例化
>>> fd = FreqDist()
# 统计文本中的词例
>>> for word in gutenbergwords('austen-persuasiontxt'):
fdinc(word)
>>> print fdN() # total number of samples
98171
>>> print fdB() # number of bins or unique samples
6132
# 得到前 10 个按频率排序后的词
>>> for word in fdkeys()[:10]:
print word, fd[word]
, 6750
the 3120
to 2775
2741
and 2739
of 2564
a 1529
in 1346
was 1330
; 1290
解答:简奥斯丁的小说 Persuasion 总共包含 98171 字和 6141 个唯一单词。此外,最常见的词例是逗号,接着是单词the。事实上,这个任务最后一部分是最有趣的经验观察之一,完美说明了单词的出现现象。如果你对海量的语料库进行统计,将每个单词的出现次数和单词出现的频率由高到低记录在表中,我们可以直观地发现列表中词频和词序的关系。事实上,齐普夫(Zipf)证明了这个关系可以表达为数学表达式,例如:对于任意给定单词,$fr$ = $k$, $f$ 是词频,$r$ 是词的排列,或者是在排序后列表中的词序,而 $k$ 则是一个常数。所以,举个例子,第五高频的词应该比第十高频的词的出现次数要多两倍。在 NLP 文献中,以上的关系通常被称为“齐普夫定律(Zipf’s Law)”。
即使由齐普夫定律描述的数学关系不一定完全准确,但它依然对于人类语言中单词分布的刻画很有用——词序小的词很常出现,而稍微词序大一点的则较为少出现,词序非常大的词则几乎没有怎么出现。任务 1 最后一部分使用 NLTK 非常容易通过图形进行可视化,如 清单 1a 所示。相关的 log-log 关系,如图 1,可以很清晰地发现我们语料库中对应的扩展关系。
如果你的代码是CPU密集型,多个线程的代码很有可能是线性执行的。所以这种情况下多线程是鸡肋,效率可能还不如单线程因为有context switch
但是:如果你的代码是IO密集型,多线程可以明显提高效率。例如制作爬虫(我就不明白为什么Python总和爬虫联系在一起…不过也只想起来这个例子…),绝大多数时间爬虫是在等待socket返回数据。这个时候C代码里是有release GIL的,最终结果是某个线程等待IO的时候其他线程可以继续执行。
反过来讲:你就不应该用Python写CPU密集型的代码…效率摆在那里…
如果确实需要在CPU密集型的代码里用concurrent,就去用multiprocessing库。这个库是基于multi process实现了类multi thread的API接口,并且用pickle部分地实现了变量共享。
再加一条,如果你不知道你的代码到底算CPU密集型还是IO密集型,教你个方法:
multiprocessing这个module有一个dummy的sub module,它是基于multithread实现了multiprocessing的API。
假设你使用的是multiprocessing的Pool,是使用多进程实现了concurrency
from multiprocessing import Pool
如果把这个代码改成下面这样,就变成多线程实现concurrency
from multiprocessingdummy import Pool
两种方式都跑一下,哪个速度快用哪个就行了。
UPDATE:
刚刚才发现concurrentfutures这个东西,包含ThreadPoolExecutor和ProcessPoolExecutor,可能比multiprocessing更简单
年前走查脚本代码时,发现大家对selenium功能都在重复造轮子,而且容易出现一些常见低级bug。于是在闲暇之余,封装一些常用的selenium功能。
在某些网页中,存在多个frame嵌套。而selenium提供的find_element函数只能在当前frame中查找,不能切换到其他frame中,需要从最上级frame中逐步切换(当然也可以指定xpath的绝对路径,但是一般没人这么做)。在我们写代码过程中,需要明确知道当前frame位置和需要寻找元素的frame位置。在frame切换过程中,容易因为疏忽导致frame切换错误导致元素无法找到的bug。
页面中分布的frame,可以理解为树状结构。因此我们可以采用递归的方式, 沿着某条搜索路线frame节点,依次对树中每个节点均做一次访问。
我们以163网址上的登录框为例:点击登录按钮,弹出登录iframe页面。输入框位置在iframe中,因此我们不能使用xpath获取元素位置,需要进入iframe中,然后获取元素。
手动切换ifame可能会产生bug,因此需要一套自动切换和检索frame的机制。具体代码如下:
需要注意的是:如果页面中多个frame中,存在相同的xpath元素。还是需要指定frame的路径,否则会返回搜索到的第一个元素。
强制等待
直接调用系统timesleep函数,不管页面加载情况一定会等待指定的时间, 即使元素已被加载 。
1如果设置的时间较长,会浪费时间
2如果设置的时间较短,元素可能没有加载。
页面中某元素如果未能立即加载,隐式等待告诉WebDriver需等待一定的时间,然后去查找元素。默认不等待,隐式等待作用于整个WebDriver周期,只需设置一次即可。
1在上文的find_element函数中,采用递归方式在所有frame寻找元素。若采用隐式等待,则在每个frame中都需要等待设定的时间,耗时非常长。
2某些页面我们想要的元素已经加载完毕,但是部分其他资源未加载。隐式等待必须等待所有元素加载完毕,增加额外等待时间。
显示等待一般作用于某一个元素,在设定的时间范围内,默认每间隔05秒查找元素。返回被加载的元素,若超过设定的时间范围未能查找则报错。显示等待作为selenium常用的等待机制,我们来看下他的源码和机制。
driver 注释中解释为WebDriver实例,但是代码中并未有相关检测,因此可以传入任何对象
但是__repr__函数中使用到session_id属性,如果需要显示属性或者转为str对象,最好在driver对象中添加session_id属性
在until函数中,我们可以看到driver对象传入method函数。在计时结束前,在不断循环执行method函数,如果method函数有正常返回值则退出循环,否则报TimeoutException错误。
可以采用装饰器对隐式等待进行封装,这样代码更加精简
同样的,采用装饰器对其他常用的函数进行封装,例如强制等待、点击、输入文本等。
装饰器虽然很方便,但也会产生一些麻烦。例如在find_element函数递归调用过程中,理应只要执行一次装饰器函数。但因为装饰器已经装饰完毕,导致每次递归都会执行。例如强制等待的sleep函数,如果递归次数越多等待时间越长。
解除装饰器一般有两种做法:一是约定参数,当递归第二次调用时则不生效。例如
这种方式实现简单,容易理解。但是增加了参数限制,在fun函数中就不能使用first_sleep参数。
二是采用装饰器采用wrapped实现,通过访问wrapped属性获得原始函数。例如
但是某一个函数被多个装饰器装饰时,需要递归解除装饰器。例如
最后整体代码如下
这次的封装其实还存在很多问题
1find_element函数不仅仅只是提供查找元素功能,还提供一些其他功能,因此叫element_operation更为合适。
2find_element函数的参数过多,并且很多参数的使用并不在函数本身中,对代码阅读很不友好。
3得小心避免参数重复问题,假设装饰器sleep和装饰器wait_time都使用time这个参数,将无法区分具体是哪个函数使用。
4不利于扩展和维护,当功能过多时find_element的参数过于庞大。
如果只是简单地封装和使用,上面这种方式也能达到较好的效果。如果想进一步封装,建议采用链式调用方式,装饰器辅助封装。例如
这样函数的扩展性和可阅读性有较大的提升
python - Poetry介绍
本文2023-09-22 04:43:54发表“资讯”栏目。
本文链接:https://www.lezaizhuan.com/article/28299.html