我的碎碎念

不为繁华易匠心,不舍初心得始终。

0%

Python的性能适合写程序吗?

AI编程课二期第一次答疑,有位同学提出了一个重量级的问题:「感觉 Python 不是很适合编写程序,因为有全局解释锁的存在和动态编译导致的性能问题,后续如何学习 c 语言或者 JAVA?」

我第一眼看到这个问题也头痛,如果我来回答,会分为两部分:Python的性能问题和如何学习其他语言,我这篇文章只针对前者进行探讨。

追求速度是人类的天性。Python在性能这一块指标肯定是拿不到前三名的,但不代表不能在TIOBE编程语言排行榜拿下每月第一名。回想起在AI时代的智识四课阅读课中,有一本最经典的计算机书籍——保罗·格雷厄姆在2004年出版的《黑客与画家》——这本书里面的一些观点就可以很好地解答Python到底适不适合写程序。我把书中几个观点提取出来,并结合自己的开发经历,略和大家讨论一二。

必须承认,如果你关注运行速度,最好使用接近机器的语言

我大学所学的电子信息工程专业,学习的第一门语言就是C语言,在我专业中需要对一些资源很小的芯片进行编程,简称单片机开发,需要使用的语言就是C语言。虽然也有些人移植了Python到单片机,但是对于商业化电子产品来说,可能并不会真正使用高级语言去开发单片机。

一个原因就是高级语言运行速度太慢,单片机是一个硬件资源受限制的开发环境,本身CPU运算速度根本达不到GHz级别,有些小家电的单片机的运行速度可能只有12MHz,如果再跑一个Python虚拟机,再在上面运行字节码,恐怕运行速度会很慢。并且这种开发更多的是属于制造业领域,需要考虑硬件成本,那么为了在单片机上跑Python,就需要好一点的芯片,于是硬件成本就上升了。

另外不喜欢用高级语言开发单片机的原因是,由于高级语言做了一层封装,所提供的接口没有C语言的SDK多,以至于没有把一些硬件资源引出来,无法进行控制,最后明明芯片是支持的功能,却无法使用。

当然,还有一些其他领域会使用C语言,比如web服务最常见的代理网关nginx,以及机器学习领域常用的numpy库的底层实现,都是使用C语言开发的。它们使用C语言不外乎以上两点原因,提高运行速度或调用硬件资源。

对于半路出家学习编程的同学来说,大多数还是做应用开发。此时,程序员的时间比计算机的时间昂贵得多

应用开发是需要编写大量的业务代码的。对于开发者来说,首先是收到需求(哪怕是自己想的灵感,也可以算是一种需求);然后理解需求,如何把这个需求进行分解,需要实现几个功能模块,有没有可以直接调用的库,还是要自己编写;最后才是实现需求,写下一行行代码。

低级语言,比如C语言和汇编语言,运行速度是很快,也能直接调用硬件资源。但是,使用它们开发效率会非常低。你需要编写大量的代码才能实现一些功能,而使用高级语言就很简单,比如同样是编写一个Hello World程序:

C语言版本的Hello World

1
2
3
4
5
6
#include <stdio.h>

int main() {
printf("Hello World\n");
return 0;
}

Python版本的Hello World

1
print("Hello World")

你看!Python是多么的简洁。此时该语言的优点就来了,开发者可以直接思考如何实现业务功能,不需要更多的心思去想语法细节。开发者的时间就这么节省出来了。

另外,保罗·格雷厄姆说:「一个功能所需的代码越多,就越难避免bug,也越难发现它们。」。写过C语言的人都知道,有时自己的程序运行达不到预期,排查来排查去,发现就是某行末尾少了一个分号。Python则没有这些问题,Python是以缩进规则表示代码块的开始或结束,而不是花括号或分号。也不会有类似Java的空指针问题,或者忘记回收内存导致内存泄漏。这些很底层的问题,Python解析器都帮你解决好了。

确实有的语言内核设计并不是很好,比如Python的全局解释锁。但是,Python有很多很强大很有用的函数库,可以用来解决开发者很多问题。在编程开发中,总有一些很琐碎普遍的问题,开发者需要花费大量的时间去解决,但是有了函数库之后,解决起来就变得容易多了。所以这些函数库比语言的核心还重要,也就是这个语言的生态。

Python是动态类型语言,也就是说,你可以给一个变量赋予整数类型,然后这个变量再赋值字符串类型,甚至一个函数也是可以的。而静态类型语言,比如C语言,则不允许你这样做,你在声明变量的时候,就需要确定好是整数类型还是字符串类型。

虽然使用静态语言这样可以防止bug,也可以帮助编译器生成更快的代码;但是这样也对程序构成了限制,无法更自由的编写程序。

自由是一把双刃剑。在Python界有一句名言:动态一时爽,重构火葬场。使用Python编写代码确实很爽,你不需要考虑任何数据类型,直接声明一个变量就可以使用,但是这个也给后期维护代码带来了麻烦。人是善忘的,前一秒你传入了字符串类型,下一秒你却当整型来处理,不怕开发时Python发出尖叫告诉你这里有bug,就怕在生产环境中直接掉进bug坑里出不来。

因此,Python在其3.5版本加入了类型提示的功能,可以给一些静态分析工具进行类型检查。之后一些优秀的函数库,比如Pydantic,你使用的时候强制需要你定义类型,也是为了保护好代码的可维护性。

与其争辩Python的性能,不如跳出圈外,关注点其他方面,比如面向对象编程

什么是面向对象编程?它和函数式编程相对应。一个程序员在接到一个要编写「毁灭地球」的任务的时候,他不会直接编写一个「毁灭地球」的函数,而是编写一个「毁灭行星」的函数,然后把「地球」传参进去。这个就是函数式编程。而面向对象编程,就是把「毁灭行星」这个函数,作为「宇宙」这个类的方法。

1
2
3
class Space:
def destroy_planet(self, planet):
...

面向对象编程的优点在于,如果你需要添加「毁灭恒星」的功能,那么只需要添加相应的方法就可以了,甚至可以不修改程序其他部分。

编程语言有静态和动态之分,对于面向对象编程,编程语言只有程度之分。目前只有两种程度的面向对象编程语言,一种是允许你面向对象编程,另一种是强迫你必须面向对象编程,比如Java,Java版本的Hello World,你必须先写一个Main的类,再写一个main方法,最后才能打印Hello World

1
2
3
4
5
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

保罗·格雷厄姆认为后一种语言是不可取的,我也是这么认为的。最好的方案是使用允许你面向对象编程的语言,最后编写代码的时候到底用不用,则是另外一个问题。这也是接下来我想探讨的。

在AI编程首期,我遇到了这么一个问题:「什么时候使用类去编程,什么时候使用函数去编程?」

我回答是,在我的开发经历当中,有几个地方是必须使用面向对象编程的。

第一个是编写ORM的代码的时候,就必须使用面向对象去编程。什么是ORM?就是数据库中的一张表映射为代码中的一个类,之后你对这个类的对象进行操作,就相当于操作这张表的数据了。

另一个是需要继承框架的一些方法才能运行的时候,比如使用PyQt编写桌面端应用程序,必须继承它的QtWidgets类,才能编写一个组件显示在界面上。

那么什么时候使用函数去编程?除了以上两种场景,其他情况都可以使用函数去编程。在web开发中,我更喜欢使用函数去实现HTTP接口,而不是类。除了实现接口,一些辅助函数,我也习惯使用函数去实现。但是如果遇到了复杂的业务规则,我就会使用类和方法来实现。因此我的一个准则是:简单的功能用函数实现,复杂的功能用类与方法来实现。

除了类与方法,还需要关注的最重要的一点就是数据库。面向对象编程可以跨语言讨论,但是SQL语言除外。千万不要拿面向对象编程的思维去设计数据表。

Python的ORM工具极其强大,你可以直接使用面向对象编程的方式,去设计一个个数据表对象,然后再迁移到数据库。但是,这是有害的!长期使用面向对象编程的思维方式去设计数据表,会忘记数据库的本质。

数据库的设计不讲究封装和继承那一套。更多考你的是如何把高维的业务规则,变为一张张二维数据表的名称和字段,以及如何更高效地查出数据,或者如何更有效地写入数据。

而且,大多数Web应用程序的并发性能压力,不是在Web框架,而是在数据库。这是你使用面向对象编程的思维方式无法解决的问题,也不是任一一个编程语言的问题。

写了那么多,如果要重点提醒初学者什么内容的话,那就是:对于半路出家学习编程的同学来说,不需要考虑Python的性能适不适合写程序这个问题。如果需要更快更强,必然是选择接近机器的语言。Python可以是你入门的第一门编程语言,但不是最后一个。除了性能,你需要更多考虑的是你的开发时间,你的数据库设计,你的软件作品。

欢迎关注我的其它发布渠道