我“接触” Python 已有十年了,当初我们要做一个网站,有个学弟只花两天,就用 Django 开发好了后台。那是我第一次感受到了 Python 的强大魅力。不过,我正式学习和使用它,才仅有两年时间……
标题中说的“从业十年”,并不是指我,而是指文章的作者。像他拥有这么长的 Python 经验的程序员并不多见,写成文章分享出来的就更少见了。所以这篇文章还挺有价值的,内容很丰富,特分享给大家,建议收藏。
一、概述
本文起源于我在 Twitter 上发布的关于 Python 经历的一系列话题。
出于某些原因,想记录一下我过去数年使用 Python 的经验和一些感悟。毕竟算是一门把我带入互联网行业的语言,而我近期已经几乎不再写 Py 代码, 做一个记录,也许会对他人起到些微的帮助,也算是纪念与感恩了。
二、摘录
推文地址:https://twitter.com/ppcelery/status/1159620182089728000
最早接触 py 是 2010 年左右,那之前主要是使用 c、fortran 和 matlab 做数值运算。当时在做一些文件文本处理时觉得很麻烦,后来看到 NASA 说要用 py 取代 matlab,就去接触了 py。
python 那极为简洁与优美的语法给了当时的我极大的震撼,时至今日,写 py 代码对我而言依然是一种带有艺术意味的享受。
私信博主001 领取完整代码!
首先开宗明义的说一句:python 并不慢,至少不够慢。拿一个 web 后端来说,一台垃圾 4 核虚机,跑 4 个同步阻塞的 django,假设 django 上合理利用线程分担了阻塞操作,假设每节点每秒可以处理 50 个请求(超低估),在白天的 10 小时内就可以处理 720 万请求。而这种机器跑一天仅需要 20 块钱。
在学习 Python 以前需要强调的是:基础语法非常重要。虽然我们都不推崇过多的死记硬背,但是少量必要的死背是以后所有复杂思维活动的基础,就像五十音对于日语,通假字和常用动名词对于文言文,你不会就是不行。
一般认为,这包括数据类型(值/引用)、作用域(scope)、keyword、builtin 函数等
关于 Python 版本的选择,很多公司老项目依然在用 2.6、2.7,新项目的话建议至少选择 3.6(拥有稳定的 asyncio)。
关于版本最后在说几点,建议在本地和服务器上都通过 pyenv 来管理版本,而不要去动系统自带的 python(以免引起额外的麻烦) https://blog.laisky.com/p/pyenv/
另外一点就是,如果你想写一个兼容 2、3 的工具包,你可以考虑使用 future http://python-future.org/compatible_idioms.html
最后提醒一下,2to3 这个脚本是有可能出错的。
学完基础就可以开始动手写代码了,这时候应该谨记遵守一些“通行规范”,几年前给公司内分享时做过一个摘要:
有了一定的实践经验后,你应该学习更多的包来提高自己的代码水平。
因为 py 的哲学(import this)建议应该有且仅有一个完美的方式做一件事,所以建议优先采用且完善既有项目而不建议过多的造轮子。
一个小插曲,写这段的 Tim Peters 就是发明 timsort 的那位。
https://en.wikipedia.org/wiki/Tim_Peters_(software_engineer)
有空时候,建议尽可能的完整读教材和文档,建立系统性的知识体系,这可以极大的提升你的眼界和思维能力。我自己读过且觉得值得推荐的针对 py 的书籍有:
如果你真的很喜欢 Python 的话,那我觉得你应该也会喜欢阅读 PEP,记得几年前我只要有空就会去翻阅 PEP,这相当于是 Py 的 RFC,里面记录了几乎每一项语法的设计理念与目的。我特别喜欢的 PEP 有:
以前听别人讲过一个比喻,静态语言是吃冒菜,一次性烫好。而动态语言是涮火锅,吃一点涮一点。
那么我觉得,GIL 就是仅有一双筷子的火锅,即使你菜很多,一次也只能涮一个。
但是,对于 I/O bound 的操作,你不必一直夹着菜,而是可以夹一些扔到锅里,这样就可以同时涮很多,提高并行效率。
GIL 在一个进程内,解释器仅能同时解释执行一条语句,这为 py 提供了天然的语句级线程安全,从很多意义上说,这都极大的简化了并行编程的难度。对于 I/O 型应用,多线程并不会受到多大影响。对于 CPU 型应用,编写一个基于 Queue 的多进程 worker 其实也就是几行的事。
(订正:应为伪指令级的线程安全)
from time import sleep from concurrent.futures import ProcessPoolExecutor, wait from multiprocessing import Manager, Queue N_PARALLEL = 5 def worker(i: int, q: Queue) -> None: print(f'worker {i} start') while 1: data = q.get() if data is None: # 采用毒丸(poison pill)方式来结束进程池 q.put(data) print(f'worker {i} exit') return print(f'dealing with data {data}...') sleep(1) def main(): executor = ProcessPoolExecutor(max_workers=N_PARALLEL) # 控制并发量 with Manager() as manager: queue = manager.Queue(maxsize=50) # 控制缓存量 workers = [executor.submit(worker, i, que