最近研究 python 的一个小失落

2016 年 8 月 1 日
 SlipStupig

python 有一个很-O 选项我一直很好奇这个选项是干嘛的, help 写的是:

 -O     : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x
-OO    : remove doc-strings in addition to the -O optimizations

python 优化选项可以产生更小的 bytecode 文件,我试着选了一下,确实小了一点,但是对性能提升并没有什么用,后来看官方邮件是这么回复的:


> Py_NoSiteFlag = 1...assuming you don't need to load site.py 
>
>     for example:</br>
>
> extern int Py_OptimizeFlag;
> extern int Py_NoSiteFlag;
> ...
> if( !Py_IsInitialized() ) {
>     Py_OptimizeFlag = 2;
>     Py_NoSiteFlag = 1;
>     Py_Initialize();

最后结论是 python 优化选项并没有什么用,想加速换 pypy

33566 次点击
所在节点    Python
268 条回复
serial
2016 年 8 月 10 日
@wizardforcel

请问你学过编译原理吗? mul(add(sub(1, 2), 3), 4) 对机器有利? 有相同的优势?

麻烦你好好读读《 Compilers: Principles, Techniques, and Tools 》先搞清楚怎么抽象和解析语法树, OK ?

mul(add(sub(1, 2), 3), 4) 对机器有利? 你在讲相声呢。
tkisme
2016 年 8 月 11 日
@dzhou121 编程语言最终是来解决问题的,选择最适合你的问题的编程语言。
所以还在争什么。
appleorchard2000
2016 年 8 月 11 日
@serial
twisted, tornado, asyncio, eventlet, gevent 这些主要就只有两种设计风格,各自风格内部是可以兼容的。比如 twisted 与 tornado 官方有为彼此兼容推出模块( http://www.tornadoweb.org/en/stable/twisted.html), eventlet 与 gevent 都是基于 greenlet ,甚至连 API 都是基本雷同。

monkey patch 是 eventlet 与 gevent 背后的惯用手法(当然你也可以不用 monkey patch, 而是对应得采用他们所提供的 io 库的非阻塞版本,标准库中的所有 io 模块对应的都有实现,包括 os, socket, queue 等等。) monkey patch 手法的手坏,我觉得仁者见仁智者见智,对我个人来说他似乎看起来是有些 evil ,但也确实在实际的使用场景中大有用武之地。

对于 asyncio 来说,与之搭配的异步 io 库基本都在这里: http://asyncio.org/。

以上几个框架,背后的 io 多路复用技术都是基于 select, poll, epool , kqueue ,实现了自己的 hub 或者叫 ioloop 。

纵上, twisted/tornado/asyncio 的 generator style 异步与 eventlet/gevent 的协程异步都可以完成自己的任务。相应的,各自阵营也都有自己完备的异步 io 解决方案,我看这里没有你所诟病的问题。至少, 基于 python 的技术栈是能够解决异步与阻塞的问题的,也是能够实现高并发的。

当然,要想在上述几个阵营之间切换就不现实了,还有 python2 -> python3 之间的升级也是问题。这是事实。

此外, 对于你所提到的几个事实,我有以下疑问:

说 openstack 自己实现了所有的异步驱动,这是啥意思, openstack 中的异步都是基于 eventlet 的 monkey_patch ,将标准库中的所有 io 行为替换为异步版本,据我所知这里不需要自行实现任何的所谓“异步驱动”。

“放弃高并发使用 django, flask ”这个说得也是奇怪, django 只是使用 python 来制作 wsgi 程序,而 wsgi 程序可以交给 apache/nginx 等来调用,此时的高不高并发,完全是由 apache/nginx 来支持的,跟 django 有什么关系。

另外 @GeekGao 说的 “ io 阻塞不阻塞跟语言没有关系”, 我不知道你是怎么理解的,使用 python 这种语言是即可以写出阻塞式的 io 程序,也可以写出非阻塞式的 io 程序,这只取决于你在调用系统调用的时候,是否指定了相应的参数而已。诚然 python 标准库中默认都是使用阻塞式的 io ,也许你诟病的就是这个?

最后,明确一下我个人的看法, python 完全是可以使用异步与高并发的, io 不是其短板, cpu 确实是(cpython)。时至今日, openstack, douban, zhihu 这些巨大软件体都基于 python 来实现的时候,我认为像 “ python 是胶水语言,是像 shell 一样的脚本语言”这样的论断,是不攻自破的。然而,这并不是我说我很喜欢 python ,与 go 语言的异步相比, 我也认为 python 真的有够折腾,有够 ugly ,而且其 GIL 导致它只能使用 ”进程 + 协程“ 的组合,而不能使用 "线程 + 协程“ 的组合,等等。 python 确实让我感觉不爽,不过没有在座某些人说的那么不堪。
appleorchard2000
2016 年 8 月 11 日
@serial 至于你提到的数据库连接库问题,忘记说。如果是基于标准库中的 socket 来实现的,基于 eventlet/gevent 要实现异步很容易,直接 monkey_patch 就可以。 反而是 MySQLdb 这种基于 C 扩展实现的模块,才有问题。这种没法使用 monkey_patch 来解决,不过 eventlet/gevent 为这种情况提供了线程包装,可以将数据库操作扔到线程之中执行,以弥补这个缺陷。
brucedone
2016 年 8 月 11 日
》翻页吧 python 慢 这个是事实,为了运维方便 从把 shell 迁到了 python ,之前分钟级别的处理变成了小时级
@YORYOR

我不知道你说的慢是哪里慢,如果仅仅是因为换了语言就说 python 导致的慢,就好比你之前骑车上班,而现在坐在车里一边大骂怎么还不开车,怎么这么慢一样。
FrankHB
2016 年 8 月 11 日
@wizardforcel 你还是没跳出套。
假设只侧重于某种性能,不依赖于另一种性能,让性能在整体上看起来不成问题,这没啥大不了的。(或者说,算是分析过了一些需求,应当赞赏?不过我觉得这是份内义务劳动……)
但一个怎么看实际上都已经通用过头的语言还用这种手段掩饰设计问题就流氓了点。
通用语言当然不是指面面俱到。一般来说通用语言需要满足不特定领域专业用户的期望,其中之一体现在工作得明显不够好的领域自觉退让,而不是赖着“能用”留下一坨不成熟可靠的解决方案,增加该领域用户的本不必要的选型成本。
技术上来讲通用和不通用没有很明确的界限,更多体现在设计者的表现的意图以及实际上扩散到了多少应用领域上并且流行了。对比一下, SQL 能干的活也挺多的,但还算是比较老实当 DSL 而不是到处刷存在感,所以姑且没有那么大问题。
当然 Python 某种意义上表现突出也不算一定就烂(其实光看技术方面的问题并没有某些 SQL 方言严重),可以用有足够相似点的实例对比来偷懒地洗。
比如你可以拿某些 Ruby 实现在某些主流性能指标上给 CPython 垫背,妥妥地。

最后,之前围绕展开的是你的其中部分观点,和 Python 其实关系不大,只是主题的关系顺便拿来当例子(虽然也不算躺枪);换其它多数语言,上面很多部分也适用。这倒是正好说明了某些观点对语言设计者存在普遍的危害,所以有必要澄清。
wizardforcel
2016 年 8 月 11 日
@brucedone shellscript 比 python 快??上数据。
brucedone
2016 年 8 月 11 日
@wizardforcel 我说的是 @YORYOR ,他说换了 python 之后从分钟级别的 shell script 到了小时级别的 python ,这个锅让 python 背,在没有看到具体数据之前,我是不服的。
serial
2016 年 8 月 12 日
@appleorchard2000

不要说些理论的东西,你有用 python 开发过非阻塞系统吗?哪怕是数据量在 1 个 GB ?

有没有想过,猴子补丁和版本维护问题。要知道,做系统是要雇佣人,雇佣设备花钱的。

你的话,让我感觉你只是个学生,并没有面临过“钱”,特别是维护所花费的“钱”的问题。
serial
2016 年 8 月 12 日
@appleorchard2000

我再说的详细一点。最简单的,投票系统,并发量:每秒 1000 次写。那么用 mysql 做用户的元数据持久层,多个 redis 做缓存层,仔细想想你的 python 开发栈怎么构建。你难道告诉我,用猴子补丁来改写 mysql 、 redis 的 TCP 层?
wizardforcel
2016 年 8 月 12 日
@serial

这是你的:

expression ::= "(" operator operand { operand } ")"

operand ::= expression | literal


这是我的:

expression ::= operator "(" operand { "," operand } ")"

operand ::= expression | literal

别不懂装懂。
serial
2016 年 8 月 14 日
@wizardforcel

你这什么狗屁玩意,拿着 BNF 在这里扯什么淡。我跟你说的是语法树, AST , Abstract Syntax Tree 。 Understand !

就你这菜鸟玩意,多少年水平也在这跟我晃荡。你写过多少代码量了?玩过 TB PB 数据吗?
serial
2016 年 8 月 14 日
@wizardforcel

(* (+ (- 1 2) 3) 4) 的语法树:

........*
......./..\
.....+...4
..../...\
...-.....3
../..\
.1...2

(* (+ (- 1 2) 3) 4),在语法扫描器扫描的时候,由左至右,遇到 ( 就表示一个数据。在 Lisp ,所有内容都是数据,或者说都是列表(这是解释器抽象上的,不是语言语法上的)。() 用来包裹一个列表。每当遇到一个 (,解释器 /编译器就会构造一个分支,层层递归。整个语言以递归构造出整个语法数,因此,这类函数式语言特别注重递归,由此出现尾递归用来优化递归性能 --- 编译成嵌套循环。

你把它写成 *(+(-(1, 2), 3), 4) 再去试试。扫描器扫描到 * + - , 就这么一个小小的表达式,多少个 delimiter 了???

一个人工智能程序,得要扫描多少个?想过没有?你让机器去生成你这种垃圾代码?你先问问当前的硬件水平够不够速度去挨个判断你这些 delimiter 。
wizardforcel
2016 年 8 月 14 日
@serial

我为啥写 BNF ??我问你机器可读性是怎么来的??规则越少越可读!现代编程语言判断多是因为 statement/expression 的规则多。只要不把规则变复杂,谁放前面谁放后面完全无所谓!

你他娘的会分析复杂度吗??你睁大眼睛仔细看看,我的 expression 只有一条规则,也就是说对每个(包括嵌套)的表达式只判断一次,懂吗??我不需要判断 delimter ,这个位置就应该是个 token ,不是就不合法,懂吗??

你的式子不照样要分析运算符??一开始的确只需要判断个括号就行了,复杂性都在后面,都被你吃了??

你对编译的理解还处于外行的阶段。只有感性认知,只是知道写成什么样是可读的,但是根本就不知道其中的原理。

会写个 AST 就代表你有本事了?你咋不把我的表达式的 AST 写出来看看一不一样??每个程序员都应该知道的东西就别拿来显摆了。
wizardforcel
2016 年 8 月 14 日
@FrankHB 给 python 垫背的多了。 php5 、 matlab 、 r 、 shellscript ,甚至还有 xpcshell 上的 js 。只要不谈二进制, python 绝对排在前头。如果说 python 的性能不如 c/c++/java 就是垃圾,那么 @serial 跑不过刘翔博尔特是不是就该去死??(手动滑稽)
serial
2016 年 8 月 15 日
@wizardforcel

"我为啥写 BNF ??我问你机器可读性是怎么来的??规则越少越可读!"

你是个傻瓜吗?机器要读懂代码,要能理解代码,要能自己生成代码! Understand !不懂就别在这胡说八道。
serial
2016 年 8 月 15 日
@wizardforcel

写过 TM 的语法扫描器吗?你把你那个代码,用 python 语言写一段语法扫描算法,我看看。你给我的感觉,就是个学生,一点像样的项目都没写过。你在公司是干什么的?还是说你压根就是个学生?
serial
2016 年 8 月 15 日
@wizardforcel

"你他娘的会分析复杂度吗??你睁大眼睛仔细看看,我的 expression 只有一条规则,也就是说对每个(包括嵌套)的表达式只判断一次,懂吗??我不需要判断 delimter ,这个位置就应该是个 token ,不是就不合法,懂吗?? "

放你的屁,你不判断 delimter ,怎么生成字符流?????? TMB 的写过扫描器吗?看过扫描器原理吗??? 老子真的忍不住骂人了。

你 MB 你家的编译器、解释器不判断 delimter 啊,草你 MB 的,你发明的不需要判断 delimter 的编译器?

傻逼!
serial
2016 年 8 月 15 日
@wizardforcel

给你这傻逼写一段扫描器解析代码,自己 TMB 复制到解释器,看输出是不是 ["*",["+",["-","1","2"],"3"],"4"]。代码使用 js 版本,阻止 V2EX 排版导致的缩进问题。完全函数式和尾递归,好好 TMB 去体会。对于人工智能程序,则不是由人来写,而是机器程序自己学习后,自己生成, understand ?机器学习只是初步。

源代码(使用 nodejs 解释器, or chrome 浏览器运行):

var text = "(* (+ (- 1 2) 3) 4)";
var at = 0;
var ch = text[at];

function next() {
if (at++ >= text.length) {
throw new Error("text tail out");
}
ch = text[at];
}

function space() {
while (/\s+/.test(ch)) {
next();
}
}

function word() {
var result = "";
while (!/\s+/.test(ch) && ch !== ")") {
result += ch;
next();
}
if (at < text.length - 1) {
next();
}
return result;
}

function parse() {
space();
if (ch !== "(") {
return word();
} else {
next();
return [parse(), parse(), parse()];
}
}

var ret = parse();
console.log("%j", ret);
serial
2016 年 8 月 15 日
@wizardforcel

输入 (* (+ (- 1 2) 3) 4),扫描后打印出 ["*",["+",["-","1","2"],"3"],"4"]。这就是字符流的构建过程。我看看你 TMB 怎么个"不需要判断 delimiter ",写一个扫描器?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/296233

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX