零到全栈 · 课程

模块 6.1:第三方库和 PyPI

并非所有的功能都要从 0 开发,学习使用第三方库,拒绝重新造轮子。


先接一下模块 5 的账

模块 5 收官时,我们留了两笔账:/api/analyze 的分析是假的,每次分析完就丢了。这个模块把它们还清。这一节,先对付第一笔:让分析变成真的。

那问题来了:“真的”,怎么算?

先说拼音。 要给任意一段中文标注拼音,就得有一张覆盖几万个汉字的读音表;这还不够——中文有多音字:“重庆”的“重”和“重要”的“重”不是一个音,“行长”“银行”“行走”里的“行”能读出花来。还得整理出海量的词组规则,才能判断在哪个词里读哪个音。

再说情感分数。 凭什么说这句话 “0.9 分,偏积极”,而另一句话 “0.5分,偏中性”?这个分不是查表查出来的,得有一个从大量真实文本里学出来的模型来判断。训练模型,得先有语料、有方法、有时间。

我们掂量一下:这两样,我们自己可以做吗?不是不能做,只是会很花时间

所以面对这种需求,正确的第一反应不该是“我怎么把它写出来”。程序员圈有个特别形象的说法——“重新造轮子”(reinvent the wheel):轮子早被造出来了,非要从头再搓一个,费时费力,还多半造得更糙。

如果有成熟的现成品,那就别自己重复造。

所以第一反应应该是先问一句:这件事,社区里是不是早有人写好了? 这一节,我们就把“找现成的库”这套动作,完整走一遍。


去哪找:PyPI,和几种查法

“别人写好的库”放在哪?这个问题我们在前端见过答案:npm 的包都放在 npm registry 上,npm install animejs 就是从那儿下载的(4.2)。

Python 世界的同款叫 PyPI(Python Package Index),网址 pypi.org——这里有几十万个包,人人可取。

怎么从里面找到想要的那一个?方式不止一种:

  • 直接上 pypi.org 搜关键词,看简介、安装命令、文档链接、版本历史;
  • 搜索引擎搜“python 中文 拼音 库”这类;
  • 或者干脆问 AI:把需求描述给它,让它推荐几个候选。

三条路都能给出“候选”,但谁都不保证候选靠谱——尤其是 AI。一是它的知识有截止时间,可能推荐过时的方案;二是它偶尔会一本正经地编出一个不存在的包名(这叫“幻觉”),甚至有坏人专门抢注这类名字、放进恶意代码。再加上 PyPI 上谁都能上传——这是生态繁荣的原因,也意味着上面质量参差。

所以立一条规矩:不管候选是搜出来的还是 AI 给的,拿到手都得自己验一遍(下一步就验)。回想 5.1 那句话——“照着别人的文档,用上别人的能力”,前提是先找到靠谱的“别人”。


用这套方法,锁定 pypinyin 和 snownlp

回到我们的两个需求,就用上面的方式查一查。

搜“中文 拼音”,或者问 AI“Python 里给中文标拼音、还能处理多音字的库有哪些”——线索都指向同一个名字:pypinyin。再搜“中文 情感分析”,答案则落在 snownlp 上。

候选有了。但先别急着 pip install——按刚立的规矩,先验。


验库:靠谱吗?能不能满足需求?

验两层。

第一层,靠不靠谱。 打开候选在 PyPI 的页面(一般带 GitHub 仓库链接),看两个快信号:

  • GitHub 星数——多少人给它点过赞,是人气和信任最直观的参考;
  • 最近更新时间——还活着吗?持续发版说明有人在认真维护;几年没动的库要谨慎。

第二层,能不能满足需求。 人气高不等于合用,还得翻开文档,拿它和我们的需求逐条对照

  • 拼音:要能带声调,还要认多音字(“重庆”和“重要”的“重”读不同音);
  • 情感:要能给出一个 0 到 1 的分数,好换算成“偏积极 / 偏消极”。

读 pypinyin、snownlp 的文档,看它们提供的函数是不是正好覆盖这些——覆盖得上,才算选对了。

看那这两个库的文档,可以去它们各自的 GitHub 项目主页

  • pypinyin:https://github.com/mozillazg/python-pinyin
  • snownlp:https://github.com/isnowfy/snownlp

打开仓库页,首屏那篇长长的说明就是 README——它相当于项目的门面、主页,库怎么装、提供哪些函数、每个函数怎么用,通常都写在这儿。(PyPI 的包页面上一般也有指向 GitHub 的链接,顺着点过去就到。)

读文档时还会注意到一个细节:它们的用法示例,经常会看到以 >>> 开头的 python 代码。

比如说 pypinyin 的 GitHub README 文档中的写法:

>>> from pypinyin import pinyin, lazy_pinyin, Style
>>> pinyin('中心')  # or pinyin(['中心']),参数值为列表时表示输入的是已分词后的数据
[['zhōng'], ['xīn']]
>>> pinyin('中心', heteronym=True)  # 启用多音字模式
[['zhōng', 'zhòng'], ['xīn']]

这个 >>> 是什么?这个问题我们等下回答,现在我们先尝试在项目中安装这两个库。


装上、试运行 —— 顺便认识 REPL

安装之前,先看提示符,确认我们的 venv 环境正确:

cd ~/zero-to-tech/backend
source .venv/bin/activate      # 确认提示符前有 (zero-to-tech)
pip install pypinyin snownlp

snownlp 背后,是别人已经训练好的模型——模型不是代码,而是别人做的训练结果,我们 import 一下就能直接用。如果对于“模型”、“训练”这些词感觉很陌生的朋友,你先不要担心,我们今天毕竟是用轮子,如果你不知道轮胎的橡胶是怎么加工的,其实也没关系。

装好之后,我们就要跑一下试试,可以写一个 python 脚本,比如说写个 pinyin_test.py 文件再运行。但是这种情况下有一种更便捷的方式值得我们学习一下——刚才文档里满屏的那个 >>>,是 Python 的 REPL(交互式解释器):敲一行代码,立刻执行、立刻出结果。终端里直接敲 python 命令即可进入 REPL(注意在 (zero-to-tech) 环境里敲——刚装的库在这个环境):

python3

回车,提示符变成 >>>,就进来了。名字不用记,体感记住就行:敲一行看一行。

先别急着 import 库,用几行最简单的代码,先感受一下 REPL 和“写脚本”有什么不一样:

>>> 1 + 1
2
>>> name = "全栈"
>>> name
'全栈'
>>> print("你好," + name)
你好全栈

注意第一行:敲 1 + 1 回车,它直接把 2 显示了出来——我们并没有写 print。这就是 REPL 和脚本最直观的区别。回想 5.3 手搓的 handmade.py:那是把一整套逻辑写进一个文件,python3 handmade.py 从头到尾一次跑完,屏幕上只会出现我们显式 print 的内容。如果上面这几行要是写进 .py 文件去跑,1 + 1name 这两行什么都不会显示;想看到 2,得写成 print(1 + 1)。而在 REPL 里,敲进去的只要是个“值”,它就顺手把结果回显出来。

一句话理清两者的关系:都是同一个 Python——.py 脚本是“把动作写全、一次跑完”,适合正式的程序;REPL 是“敲一句、答一句”,适合把玩、试错、快速验证一个想法或一个新库。今天验库,正是 REPL 的主场。

好,手感有了。先验 pypinyin,正好对着刚才那两条需求:

>>> from pypinyin import pinyin
>>> pinyin("你好")
[['nǐ'], ['hǎo']]
>>> pinyin("重庆")
[['chóng'], ['qìng']]
>>> pinyin("重要")
[['zhòng'], ['yào']]

注意“重庆”和“重要”——同一个“重”字,在“重庆”里读 chóng、在“重要”里读 zhòng,pypinyin 都判对了。 它不光认多音字,还能看词定音。文档承诺的、我们需求要的,对上了。这背后就是那张几万字的读音表加词组规则,别人替我们整理好了,import 一下就能用。

顺带看清 pinyin 返回的形状:[['chóng'], ['qìng']]——一个 “嵌套列表”。这样读起来不太直观。我们的项目只想要一串干净的拼音,用不上这层嵌套。pypinyin 这个库也有对应的方案,另给了一个更省事的函数 lazy_pinyin

>>> from pypinyin import lazy_pinyin
>>> lazy_pinyin("重庆")
['chong', 'qing']

lazy_pinyin 的区别在于:① 每个字只给一个音、不再套那层列表,直接是扁平的字符串列表;② 默认不带声调chong 而非 chóng)。

如果我们还需要声调,可以给 lazy_pinyin 加上 style=Style.TONE,声调就回来了:

>>> from pypinyin import lazy_pinyin, Style
>>> lazy_pinyin("重庆", style=Style.TONE)
['chóng', 'qìng']

Style 是 pypinyin 提供的一组“拼音样式”开关,Style.TONE 就是“带声调符号”这一档。

记住 lazy_pinyin(text, style=Style.TONE) 这个写法,下一节直接用它。

再验 snownlp:

>>> from snownlp import SnowNLP
>>> SnowNLP("今天的风很轻,适合把想法写下来").sentiments
0.9465...
>>> SnowNLP("太失望了,再也不来了").sentiments
0.0027...

sentiments 给出一个 0 到 1 之间的分数:越接近 1 越积极。第一句 0.94,第二句 0.003——这不是查表,是包里那个别人训练好的模型在“判断”。跑出来的小数位可能略有出入,这也是正常的。

两条需求都验证通过,就可以退出 REPL:

>>> exit()

以后拿到任何新库,都可以尝试进 REPL 玩两下——敲一行看一行,比闷头读半天文档更快建立手感。(REPL 平时还能当计算器、当小试验田,随开随用。)


顺带认识这片生态:中文 NLP

pypinyin 和 snownlp,都属于同一片生态——中文自然语言处理(NLP,让程序“处理人话”的那一类技术)。这片地界上还有一张常见的库,值得认一下:

  • jieba(结巴分词):把一句话切成一个个词——“我来到北京清华大学”切成“我 / 来到 / 北京 / 清华大学”。分词是很多中文处理的第一步(搜索、统计词频、做词云……都先得切词)。我们的项目用不上,认得名字就行,不安装。

不过值得我们了解的是:每个领域都有自己的一片生态——图像处理、爬虫、数据分析、AI……套路全是今天这一套:找库 → 验库 → REPL 玩两下 → 接进项目。这一节学的不只是这两个库,而是这个套路。


记上账:requirements.txt

现在两个重要的库安装好了,最后别忘了那两条老规矩(都是 5.2 立的):.venv 不进 Git,但是 requirements 清单要进。库变多了,我们要重新在 requirements 中记账:

pip freeze > requirements.txt

这样就把 pypinyin、snownlp 这两个依赖都记进来了,包括它们的依赖。

这份 requirements 清单的意义是任何人拿到这个项目,只需要——

pip install -r requirements.txt

一条命令,整个环境原样复现。“任何人”这三个字里,也包括后续模块 7 时站在服务器上的我们——到时候我们会亲手体会这份清单值多少钱。(对照老直觉:它就是后端的 package.jsonpip install -r 就是后端的 npm install。)

顺手把改动提交了——这一节代码一行没动,只有 requirements.txt 变了,正好是一次干净的提交。


这一节应该带走什么

  • 我们写不出来的能力,生态里大概率有——面对需求,先找库、别急着自己重新造轮子;这一节学的是套路:找库 → 验库 → REPL 玩 → 接进项目
  • 去哪找:PyPI(≈ npm registry,Python 的公共仓库),外加搜索引擎、问 AI 都行——但谁都能上传,候选必须自己验
  • AI 只给线索:可能过时、甚至幻觉出不存在的包名——名字它出,靠不靠谱我们验。
  • 怎么验:GitHub 星数 + 是否还在维护(靠不靠谱),再读文档对照需求(能不能满足)。
  • REPLpython3 进,敲一行出一行,exit() 出——装完新库先来这儿玩两下。
  • pypinyin 认多音字、能带声调,snownlp 给 0~1 情感分(包里带着训练好的模型)——都验过了,但还没接进项目,下一节换芯。
  • requirements.txt 重新 freeze——pip install -r 一条命令复现环境,模块 7 的时候我们会依赖这个文件。
  • 顺带认脸:jieba(分词)——中文 NLP 生态的常客。

下一节,把这两个库装进 /api/analyze——只换内部实现,不动接口约定,前端一行不改,分析就变成真的了。


← 上一节:模块 5.5 前后端联调与 CORS | 下一节:模块 6.2 让网页真的会分析文字 →