Skip to main content

Command Palette

Search for a command to run...

重识 Python

Updated
3 min read

我与 python 的渊源

记得当年 python 是自学的,至今还记得入门的那本书的名字叫做《a byte of python》

2014年入行时即写 python,后端 djang 应用,当时 python 版本还是2.7,现在都进化到 3.12了,变化不可谓不大。印象中最深刻的地方(有记忆)在于 python2.7 的 print 支持以下两种写法:

Python 2.7.18 (default, Apr 20 2020, 19:34:11)
[GCC 8.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print("hello")
hello
>>> print "hello"
hello

而在 python3.x 中,仅支持以下一种

Python 3.9.18 (main, Aug 24 2023, 18:16:58)
[Clang 15.0.0 (clang-1500.0.40.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print("hello")
hello
>>> print "hello"
  File "<stdin>", line 1
    print "hello"
          ^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print("hello")?
>>>

后面的工作技术栈转向了 golang 和云原生相关,2019年因为工作的关系,接触过 appium,写过不到半年的 python,后面能用到 python 的地方只有处理一些字符串、表格等临时的活儿……

人生苦短,我选 python

最近公司人员变动有点大,组里的项目也不太行的样子,在此大背景下,我被派去支持隔壁 AI 项目组的一个 poc:

  1. 拿到了一个 java 写的服务 repo (项目开发人员即将于两天后离职)

  2. 一个阿里云服务器 ip、用户名、密码,用来部署 1 中的 java 服务

  3. 被拉进若干群

  4. TL给出的 10 分钟不到需求介绍

  5. 工作日每天下午 5 点半同步进度

  6. 有一位博士负责大模型调优

  7. deadline 为5月底

这就是全部,彼时,我手头还有其它项目支持的事情,说焦头烂额有点过,但也相差不多。虽然时不时还有其它人或事会打断我,但总归是有相对整块的时间来看遗留下来的 java 代码,梳理了一下这个 java 服务

  1. 提供了一个 post 接口,使用 SSE 模式, 用于客户方调用 ,接口有签名检验,但是代码中签名检验部分直接放过了……

  2. 调用了客户的一个 post 接口,接口同样要做签名

  3. 调用了百度千帆的 API 接口

功能还是比较简单的,困难的地方在签名规则的实现,没有详细的的文档参考;然后调用百度千帆的 API 接口这事吧,有点七弯八绕的:为啥不直接用 sdk? 问了下当时的开发,答曰:做这个项目的时候, 千帆的 java sdk 功能不全。通过翻阅官方文档发现:千帆的一众 sdk 中,对 python 的支持度是最好的。这也难怪,都要做 AI 了,没理由不用 python 啊,此时已经萌生了用 python 重写这个服务的想法。因为博士大模型调优的代码使用 python ,而我需要调用博士调优后的大模型的代码,这下没理由不用 python 了。在某天的工作进度同步会上,提出了这个设想,一帮指挥官直接说:可以啊,用啥不重要,能实现就行……

python 实现

python 版本选择

首先必须是3.x,3.12 都发布一段时间了,本着用新不用旧的原则,那就 3.12 吧(虽然我知道千帆的 python sdk 的版本是 3.9)。后面由于博士的代码是基于 python 3.9 开发的,我调用他的代码时出现了奇怪的包依赖错误,为了减少不必要的麻烦,老实切换到了 3.9, 然后就好了……

服务端框架选型

  • django: 以前写过,对于这个需求而言,django 显得略微有些臃肿

  • fastapi:在推上,fastapi 的出镜率很高,据说很强、很快

  • webpy、flask、bottle……听过但没咋写过的据说很轻巧的框架

如此随意,选了 fastapi,根据官方文档快速启动了,对 swagger 的支持很棒,这是我最喜欢的特性之一

依赖管理

  • requirements.txt: 当前我用的还是这个

  • pdm: 这个在我的信息流里面出现的频率也很高

pdm 很新很流行,但我在容器化部署时遇到一些奇奇怪怪的问题,彼时也没有耐心细细读文档,最后还是用了 requirements.txt 这种方式

静态检查

Ruff 的大名已经听说过很多次了

An extremely fast Python linter and code formatter, written in Rust.

主打一个快,用 Rust 编写,就是它了。

java -> python

这个时代有了各种免费的 AI 真好,将 java 函数扔给 AI 转成对应的 python 实现就好了,虽然代码有点丑陋,但是,能用。于是在 AI 的加持下,快速糊出了一个调用客户的接口及其签名实现,从这块开始的考虑很简单:

  • 因为是调用客户的接口,便于测试

  • 接口签名规则和算法是一致的,这个接口通了,则提供给客户的接口也就没问题了

写接口测试用例,使用 unittest 一顿操作猛如虎,写了三个 test case。

# test_push.py
import unittest
​
class TestPush(unittest.TestCase):
    def test_answer(self):
        ……
        self.assertEqual(push(input), 200)

执行

python3.9 -m unittest -v test_push

讲真,跑测试用例的感觉是真爽,虽然中间遇到一些问题,但都一一解决了。

于是找客户那边的对接开发索要了几个接口入参样例,先自测了签名验证的接口。不得不说,客户那边对接的开发同学蛮好的,给我提供了很多帮助,最终顺利完成了两个接口的本地自测。

类型恐惧

我是先用 python2.7,然后用 go,现在又写 python,我发现没有类型检查,内心还是有些不安的,比如以下代码

from pydantic import BaseModel
​
class ExampleModel(BaseModel):
    one: str
    two: int
​
if __name__ == "__main__":
    # two 字段的类型为 int
    em = ExampleModel(one='1', two='2')
    print(em.two)

竟然能正常输出:2

再看一个例子

from pydantic import BaseModel
​
class ExampleModel(BaseModel):
    one: str
    two: int
​
if __name__ == "__main__":
    # 我根本没有定义 three 字段
    em = ExampleModel(one='1', two='2', three=True)
    print(em.two)

还是能正常输出:2

毕竟,one: str 这些只是类型注解,不会在运行时执行数据验证或解析。记忆中我还是用过 python,然而,现在又感觉似乎完全不认识一样。如此情境,我只能增加测试用例,加强静态检查以及自测来补足隐藏的没有执行过的 python 代码。

fastapi 的优点

接口和入参定义真的做的蛮好

class RequestBody(BaseModel):
    app_id: str
    request_id: str

@app.post("/query")
async def example(
    body: RequestBody,
    timestamp: Union[str, None] = Header(alias='X-Signature-Timestamp', default=None),
):

header 和 body 的表达很简洁,当然,对swagger的支持也相当友好

fastapi 容器化

到这个环节,真切体会到了 go 的优势:编译后将可执行文件扔到镜像中即可(虽然在推上看过搞编译器的同学对 go 颇有微词)。来看看 fastapi 的 Dockerfile ,官网示例如下:


# 
FROM python:3.9

# 
WORKDIR /code

# 
COPY ./requirements.txt /code/requirements.txt

# 
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

# 
COPY ./app /code/app

# 
CMD ["fastapi", "run", "app/main.py", "--port", "80"]

如果是本地打镜像,记得加上 .dockerignore 文件

**/__pycache__/
*.pyc
*.pyo
*.pyd

就笔者的需求而言,感观上容器化不如使用 python 虚拟环境,因为要把代码 py 文件加到镜像中让人挺难受。长期习惯了 go 应用轻巧的镜像, python 打出来的镜像动辄就 GB 级,属实有点难以接受了。

总结

python,作为我步入职场的初恋编程语言,由于自己长期的冷落,突然对其产生出一种陌生的感觉,总归还是动手少了。python 一直在持续迭代进步,而我已经固步自封了,有一种悲凉之感。不管怎么说,学习还是让人快乐的,要做到真正的终身学习,学以致用,并不简单。突然想起之前带我的一个 TL 对我说的一句话:要勇于把手弄脏,共勉。

54 views

More from this blog

2025: 祛魅 灰度 念头通达

今天是2025年的最后一天,当大家都在准备下班的时候,好巧不巧的,我刚好发现了一个不大不小的问题,大胆猜想,小心求证,向上反馈,暴露风险,作为2025年工作注解,实在是再有趣不过了。 今年的工作,从结果上看,还算平稳,至于过程,有太多不可言说的部分。厂里打镙丝的牛马,有工资可拿,理应知足了,至于其它的,与己无关,也没那么重要了。 祛魅 近距离观察大厂,才发现一些违背常识/直觉的事实:路人以为的高大

Feb 28, 20261 min read21

大厂祛魅:破碎的专注力

毁掉一个人最直接的方法,就是毁掉ta的专注力。 这句话的出处已然模糊,但放在大厂环境中,却显得格外深刻。 围城 大厂宛如一座围城。城外的人满怀憧憬,目之所及皆是光鲜;城内的人却如困笼之鸟,翅膀日渐退化,每日挣扎求生。 高大上 不可否认,大厂的硬件设施确实令人艳羡:宽敞的独立园区内,来往穿梭的人群中,几乎人人手握智能设备。这看似现代化的景象背后,却藏着一个无奈的事实:在工作时段,每台电脑都被严密监控,连听音乐都成奢望。于是,工作之余玩手机,成了许多人难得的解压方式。 大厂的品牌效应确实强大。外界对...

Jul 29, 20251 min read138

Black Swan

黑天鹅理论 是指极不可能发生,实际上却又发生的事件 来到大厂打工已经满一个月了,从一开始的手足无措,到逐渐度过不适期,也算是适应了吧。 不适应 刚入职时,不适应的地方还是挺多的。 第一次只使用台式机工作,这就限制了我一天中的绝大部分时间,都必须呆在自己的工位上,好在工位足够大。只是人与人的沟通少了很多,有问题只能在工位上通过 IM 呼对方,有种魔幻又现实的感觉 第一次只能用 Windows,也不能 WSL,这给我的工作效率带来了很大影响,不能用熟悉的软件,就连写代码用的 VSCode 的...

Jan 24, 20251 min read74

2024年: 逐渐平静

这个世界是一面镜子,会把你的感受反射给你 2024 开端: 相由心生 那时,还带着一着愤懑,因为拿到了低绩效,虽然内心知道这是公司经营困难,想让我离开的一种策略,但仍然感受到自己那可笑的自尊受到了践踏。自那之后,非必要不加班,只做份内事,尽可能地不去涉及份外之事。 2024 年中: 与人为善 组里的项目眼见不行了,我被迫去支援 AI 项目,久违地写起了 python,项目接近完成时,意外收到通知:我拿到大礼包了。在这之前,架构师因故裸辞。在我离开之后不到两周,我的 TL 也裸辞了,直到同事告诉...

Jan 9, 20251 min read92

企业软件之殇

殇 动词 未成年而死。 名词 战死者。 笔者经历了两家打着云原生旗号的企业软件/解决方案公司,都是中途加入,项目都以解(失)散(败)告终。 云原生解决方案 NB 公司:一个传统的 IDC 小厂,想着借云原生的热度,进军企业软件市场。 在加入这个项目之前,笔者考取了 CKAD 认证,彼时对 K8s 相当着迷。先简要介绍一下这个项目背景: 基于 Rancher (换皮肤)的二次开发项目,名字叫:HCaaS ,在笔者加入这个团队之前,项目已经开发近两年了,除了 TL 之外,其它人之前都...

Jul 1, 20241 min read103

just for fun

57 posts

I'm a Software Engineer

重识 Python