程序员如何写PPT

这个世界上有两种文件,一种是二进制文件,一种是文本文件。虽然本质上都是编码成二进制存储在磁盘上,但是文本文件通过编码规则转换成二进制,可以很方便的通过几乎任何程序解码出来。文本文件是给人读的,二进制文件是给机器执行或载入的。所以如果二进制文件拿来给人读,这就很反人类了。像 Office 的 .doc .xls .ppt 就是这么一些反人类的玩意儿。

幸运的是人们发明了 markdown, html 等可以用纯文本表示出复杂的格式。如果是文本文件,很多事情就变得方便了:你可以使用几乎任何浏览程序或者编辑程序打开;你可以使用 git 对这些文件进行版本控制;可以为他们制作一个自动集成的系统(比如格式检查等);纯文本的格式是确定的,只要遵守格式规范来编写,不像是 .ppt 文件被 Keynote 和 PowerPoint 打开,显示效果可能是不同的;最重要的是:你可以使用你最喜欢的 Vim 编辑器编辑他们!配合适用各种类型的插件,你就可以编辑世界上的任何东西——只要它们是文本文件。

这篇文章会介绍一些不使用微软的 PowerPoint 来制作演示文稿的方法,好处呢,就是上面所介绍的那一些(完美的纯文本),坏处呢,就是可能会有一个 build 的过程,无法所见即所得了。

1 使用 gitpitch

这个是最简单的一个,你只要在 Github 上面上传一个 markdown 文件,然后通过这个网站打开,就自动变成一个 Slides 了。只不过你的 markdown 要稍微对 gitpitch 做一下兼容。其实这个我之前在博客分享过,在公司分享过一次 tmux 就是用这个东西做的 Slides 的。

更新一下评论的 Lisp 大神推荐的 Marp,跟这个思路差不多,也是将 markdown 渲染成演示,但是是本地的编辑器,所见即所得。

2 使用 Jupyter

可能做数据方面的同学比较熟悉 Jupyter,这种基于网页的 REPL 比较好的地方就是渲染图表的时候可以直接显示。Jupyter 功能非常强大,直接运行代码、markdown格式、做图表样样都行。做 Slides 展示也很好看。看过一个同事分享的,效果非常不错。使用教程可以参考这篇。(上图亦来自这篇教程)

3 Read the docs

第一次见到有人使用 readthedocs 做分享是在 Raymond 的视频中(上一篇博客提到过)。readthedocs 显示非常简洁,渲染出来的代码块也非常漂亮,在 Chrome 下按 Command + + 就可以放大,效果很好。左侧的可以嵌套的目录正好可以做一个大纲展示。readthedocs 真是简洁又实用,我翻译的一本书也放在上面,效果可以作为参考。

这种方式的一个额外的好处是,随便找一个文件托管服务,比如 S3 bucket 或者 github pages 就可以,然后随便一台电脑就可以打开访问演示。而且这样放在网上访问方便,不会像 .ppt 文件不兼容新版啦等问题,作者如果更改可以一下子同步上去,PPT 这种文件就像泼出去的水,发给别人就再也更新不了了(当然也可以“最终版” “再也不改了” “最后版本” “最最后版本”发给别人哈哈)。缺点就是要学习 sphinx 学习成本可能会高一些。

4 Racket SlideShow

Racket 套件

Racket 套件

这个是我偶然发现的,没有实际用过。安装 Racket 之后竟然出现这么多套件,有游戏,还有一个 SlideShow???看了一下,竟然是用 Racket 代码来写 PPT…… 听说貌似还有不少用户。

Racket源代码

好吧,Lisp 程序员的世界我有点不懂……

另外再赠送一个呗。推荐一个画图的工具,Graphviz。这个有 Python 的封装,可以使用在项目中,也可以直接手写文件画,还蛮好看的。这里有个画 Python 继承关系的例子。可以看下。

miloyip老师画的

我最初知道这个还可以拿来直接当作图工具用是看了 miloyip 老师的推荐,有兴趣可以看一下这个回答:Mac 上最好用的流程图软件是什么?

 

Python 提高对比效率的一个技巧(位运算&)

最近在看 Python epoll howto 的时候,发现里面对比一个 event 是不是某种 EVENT 类型用的是 & 操作符,比如 elif event & select.EPOLLIN 。一开始还以为是写错了,后来想了一下这样确实能起到 == 的作用,只要事件的实际数字值特殊设计的话。

只比较相等的话,只要不存在包含关系就可以。比如最简单的,每一个 bit 的1表示一个类型(这里都以一个 bytes 作例子吧。

上面4个数字分别是1,2,4,8,分别可以表示四种类型。对比的时候,除非两个类型相等,否则都是 0(Flase)。或者可以用下面这种方式,使用两个1,可以表示的类型多一些。

上面这种也可以达到类似的效果。如果存在包含关系的话就不行了,在不相等的情况下依然可能得到非0值。但是如果不是比较相等的情况,而是比较是不是包含,就可以使用这种表示了。比如 Unix 的文件权限系统,用 4 2 1 来表示执行:

如果验证 6 是否有读权限的话也可以用 & :

结果是非0值,表示拥有此权限。

为什么这么写呢,估计是C程序的风格吧,我用 Python 测试了一下性能,& 操作是比 == 效率高的。因为 epoll 这部分的代码是非常频繁的操作,能节省一些性能的话,还是很可观的。

通过反编译发现,其实 & 编译成 Python 字节码是 BINARY_AND ,而 == 是 COMPARE_OP。应该是 BINARY_AND 效率会比 COMPARE_OP 效率高一些吧。

 

 

PyCon2018 Review (Part 2)

最近空闲时间在看 PyCon 的一些分享,2018年的视频好多,不知道啥时候才能看完。这里将一些个人看过比较好的记录一下。上一次的文章在这里:PyCon2018 Review (Part 1)

5. GC再见,GC你好——Instagram Django进程调优案例

当初 Instagram 禁用 GC 的那篇文章发布之后,声名大噪。Zekun Li 在大会上讲了一下 Instagram 在 Python GC 上下的功夫。起先因为 GC 回收循环引用的问题,共享内容用的少,fork 出来的进程会占用很多内存,于是 Instagram 就想办法禁用了 GC,这样可以减少 Copy-on-Write 需要复制的内存,当然禁用了 GC 肯定会有内存泄漏的问题,Instagram 的选择是保证代码质量,避免在代码中使用循环引用。然而虽然项目增长,这样严格的代码质量难以保证,Instagram 又打开了GC。

演讲者听口音应该是大陆人,这个视频有助于对内存的理解,值得一看。

推荐指数:5

演讲者:Zekun Li from Instagram

相关项目:Django, uWSGI

Youtube: Zekun Li – There and Back Again: Disable and re-enable garbage collector at Instagram – PyCon 2018

6. 一份简单的 Pythonic 指南

我见过很多人写的带有“Java味道”的 Python 代码,里面很多复杂的抽象和继承关系;在循环里自己维护下标等行为,实在头疼。这个演讲适用于新手或者从其他语言过来的人,可以快速过一下 Python 的一些不错的特性。对于写了很长时间的 Python 程序员来说,可能用处不大。吐个槽:整天被业务赶着,时间长了危害实在是大,不抽出时间看看自己的工具都有什么不错的功能,就永远不会有什么长进。

演讲者来自微软,是一位女性,比较好懂。

推荐指数:4

演讲者:Nina Zakharenko

相关项目:The Zen of Pythonagithub, freezegun

Youtube: Nina Zakharenko – Elegant Solutions For Everyday Python Problems – PyCon 2018

7. 打一场值得打的仗:Facebook 的 Python 3迁移之路

现在 Facebook 的大多数项目都是运行在 Python3.6.3 上面的,但是到 2014 年,在 Facebook 内部使用 Python3 几乎是不可能的事情,大部分工程师都持 “我们将永远使用 Python2.7 的态度。一小部分人用业余时间驱动了公司向 Python3 迁移,看本篇演讲,学习如何在你的公司打一场值得的仗。变化不会自己发生,必须有人来引导做出改变。在迁移的过程中,作者首先面对的是 Facebook 所有的 build 系统都是针对 Python2.7 的,只好去一个一个修复,兼容 Python3.然后又要面对同事不停地提交 Python2 代码,作者又加入了 pyflake 检查。(一个小技巧,如果你在你们公司装的很权威,那么别人就会以为你在这方面是权威人士)

后来一些项目只是运行了 2to3,没做其他改变,结果导致因此占用内存降低了一半,运行速度提升了 40%。

Educate for the future you want, not the present you have.

美中不足的是,演讲者经常停顿和重复,对于我来说理解起来不是那么简单。

演讲者:Jason Fried

推荐指数:4

相关项目: 2to3, pyflake

Youtube: Jason Fried – Fighting the Good Fight: Python 3 in your organization – PyCon 2018

8. 字典技术的演化

这个视频是 laike9m 上次在线下给我推荐的,同样不是一个今年的 Pycon 视频,但是号称是“质量最高的一个演讲”,回来看了一下,真的很不错。

大家可能都有听说,Python3.6 里面字典的速度和内存都有了提升,同时带来一个副作用:默认有序了。并且这个副作用在 3.7 版本中写入了语言标准。Raymond 这个演讲涉及了字典的很多常见的技术,例如 Key Sharing,Open Addressing,Double Hashing 等,从头到尾展示和这些技术的演化过程、性能和原理等,介绍了 Python 的 dict 从一开始浪费内存、速度慢进化到了今天的甚至对 key lookup 有缓存。Python 基本是运行在字典上面的,即使有时候我们的感知很弱:module 是一个字典,global 是一个字典,Django 是运行在无数的字典上的,所以如果你优化了字典,你就优化了 Python。相比于 Python2.7,如果你不用 AsyncIO,你可能没有特别的理由升级到 Python3.5,但是 Python3.6 提升了 dict 的性能,这就很划算了。新的 dict (compact dict)使用 list 来存储字典的 key(这样空的位置只需要 8bytes 就够了),减少的空间同时也可以加快 hash 表的载入速度。字典还涉及到一些细节问题,比如删除元素,最后的 QA 也有很多干货。

有意思的是,Raymond在2016年就提到:Guido 目前还认为字典的 key 保持插入顺序这个特性还不能被依赖,但是他会被说付的,简直是预测帝啊。

演讲非常搞笑,Raymond 对自己的 Compact Dict 太满意了哈哈哈哈哈。

另外,我之前也花了很多时间研究字典一些相关的问题,看视频之前也可以看一下我写的这两篇博客,可能加深你对字典的理解,我自认为写的还是比较好懂的:《Hash碰撞和解决策略》、《Python 为什么list不能作为字典的key?

演讲者:Raymond Hettinger(资深Python讲师)

推荐指数:5

相关项目:builtin dict

Youtube:

一共找到两个版本,建议都看一下,67分钟版本更早,里面比较轻松,PyCon 那个比较正式,更加条理。不过67分钟版本的后面有很多 QA,也很有价值。

  1. PyCon2017 37分钟版本:Raymond Hettinger Modern Python Dictionaries A confluence of a dozen great ideas PyCon 2017
  2. 2016年的67分钟版本:Modern Dictionaries by Raymond Hettinger

最后赠送一个我最近在听得一个 podcast(macdavid推荐的):teahour ,内容不错,挺丰富的,从区块链到编辑器都有,很适合程序员。

 

Python3.7 dataclass 介绍

Python3.7 加入了一个新的 module:dataclasses。可以简单的理解成“支持默认值、可以修改的tuple”( “mutable namedtuples with defaults”)。其实没什么特别的,就是你定义一个很普通的类,@dataclass 装饰器可以帮你生成 __repr__ __init__ 等等方法,就不用自己写一遍了。但是此装饰器返回的依然是一个 class,这意味着并没有带来任何不便,你依然可以使用继承、metaclass、docstring、定义方法等。

先展示一个 PEP 中举的例子,下面的这段代码(Python3.7):

@dataclass 会自动生成

引入dataclass的理念

Python 想简单的定义一种容器,支持通过的对象属性进行访问。在这方面已经有很多尝试了:

  1. 标准库的 collections.namedtuple
  2. 标准库的 typing.NamedTuple
  3. 著名的 attr 库
  4. 各种 Snippet,问题和回答

那么为什么还需要 dataclass 呢?主要的好处有:

  1. 没有使用 BaseClass 或者 metaclass,不会影响代码的继承关系。被装饰的类依然是一个普通的类
  2. 使用类的 Fields 类型注解,用原生的方法支持类型检查,不侵入代码,不像 attr 这种库对代码有侵入性(要用 attr 的函数将一些东西处理)

dataclass 并不是要取代这些库,作为标准库的 dataclass 只是提供了一种更加方便使用的途径来定义 Data Class。以上这些库有不同的 feature,依然有存在的意义。

基本用法

dataclasses 的 dataclass 装饰器的原型如下:

很明显,这些默认参数可以控制是否生成魔术方法。通过本文开头的例子可以看出,不用加括号也可以调用。

通过 field 可以对参数做更多的定制化,比如默认值、是否参与repr、是否参与hash等。比如文档中的这个例子,由于 mylist 的缺失,就调用了 default_factory 。更多 field 能做的事情参考文档吧。

此外,dataclasses 模块还提供了很多有用的函数,可以将 dataclass 转换成 tuple、dict 等形式。话说我自己重复过很多这样的方法了……

Hook init

自动生成的 __init__ 可以被 hook。很简单,自动生成的 __init__ 方法会调用 __post_init__

如果想传给 __post_init__ 方法但是不传给 __init__ ,可以使用一个特殊的类型 InitVar

不可修改的功能

Python 没有 const 类似的东西,理论上任何东西都是可以修改的。如果非要说不能修改的实现呢,这里有个比较著名的实现。只有不到10行代码。

但是有了 dataclass ,可以直接使用 @dataclass(frozen=True) 了。然后装饰器会对 Class 添加上 __setattr__ 和 __delattr__ 。Raise 一个 FrozenInstanceError。缺点是会有一些性能损失,因为 __init__ 必须通过 object.__setattr__ 。

继承

对于有继承关系的 dataclass,会按照 MRO 的反顺序(从object开始),对于每一个基类,将在基类找到的 fields 添加到顺序的一个 mapping 中。所有的基类都找完了,按照这个 mapping 生成所有的魔术方法。所以方法中这些参数的顺序,是按照找到的顺序排的,先找到的排在前面。因为是先找的基类,所以相同 name 的话,后面子类的 fields 定义会覆盖基类的。比如文档中的这个例子:

那么最后生成的将会是:

注意 x y 的顺序是 Base 中的顺序,但是 C 的 x 是 int 类型,覆盖了 Base 中的 Any。

可变对象的陷阱

在前面的“基本用法”一节中,使用了 default_factory 。为什么不直接使用 [] 作为默认呢?

老鸟都会知道 Python 这么一个坑:将可变对象比如 list 作为函数的默认参数,那么这个参数会被缓存,导致意外的错误。详细的可以参考这里:Python Common Gotchas

考虑到下面的代码:

将会生成:

这样无论实例化多少对象,x 变量将在多个实例之间共享。dataclass 很难有一个比较好的办法预防这种情况。所以这个地方做的设计是:如果默认参数的类型是 list dict 或 set ,就抛出一个 TypeError。虽然不算完美,但是可以预防很大一部分情况了。

如果默认参数需要是 list,那么就用上面提到的 default_factory 。

 

参考资料:

  1. PEP 557
  2. dataclass标准库文档
 

推荐一本计算机基础的科普书:《穿越计算机的迷雾》

小时候经常想,计算器是如何将两个数字加起来的?难道计算器保存了所有的个位数相加的表吗?为什么能将数字显示出来?为什么只能做简单的操作,不能做混合运算(后来知道了一种叫“科学计算器”的东西)?

后来学到了计算机内部使用二进制表示的,但是小时候那种困惑依然没有解决。这个二进制到底是如何应用的?内存里面是有很多点记录了0和1吗?那这些点有多小呢,才能保存那么多数据?这些数据在计算机内部又是怎么取出来的,怎么使用的?

直到同事给我推荐了《穿越计算机的迷雾》这本书,这些困扰我十几年的问题才渐渐明了。当然,如果大学的时候选修计算机组成原理好好上一下的话,可能也早就明白了。对计算机感兴趣,或者对于计算机到底是如何工作还有疑问的同学,非常推荐读一读这本书。这本书语言非常通俗,虽然有些地方需要仔细思考才能跟上,但是作者已经是非常努力的将这些难的东西写明白了。有些地方可能有些啰嗦,像《鸟哥的Linux私房菜》一样,但是也可以当做是幽默吧。

计算机的发明是一个非常漫长的过程,这段历史中有很多伟大的科学家的努力。首先人类先是发现了电,然后发现了电流的磁效应,法拉第又发现了磁能生电,接着开启了用电磁来传递信息的漫长探索之路,摩斯密码,电报,电话因此诞生了。

让计算机做加法这一个简单的操作,也经历了漫长的探索过程。这又和古老的逻辑学分不开,有了逻辑上的真值表和推论,才有可能用二进制表示逻辑问题,将数字逻辑转换成逻辑电路。通过一番精彩的推导,就可以制造出一个电路复杂的全加器。到这里我也对二进制有了一个更深刻的理解:计算机的计算方式和人类是有本质的不同的,人类可以理解复杂的事情,可以处理十进制。计算机天生就是处理很简单的东西,然后使用很简单的东西组合出来很复杂的东西。比如说,计算机当然也可以用十进制,不同的电压(比如0~10v表示1,11~20v表示2……)来表示。但是这样的问题是,我们要设计电路表示1+1=2,1+2=3等等。如果用二进制,只有很有限的几种情况:1-1 1-0 0-0。这种枚举起来简单,大大的简化了电路的设计。

这基本就是 CPU 的原型了:能做简单的计算。借着触发器问世,多个触发器组成的电路可以保存中间结果,支持读写。为了读取这些触发器保存的值,又出现了地址译码器,这样无论读写哪个地址都可以很快,也很简单。这就叫做随机读写(RAM)。

这些基本就是现代计算机的老底了。接下来就是我们熟悉的汇编指令,网络内容等。可以说,电子计算机的发明并不像电灯泡、轮子这种东西一样,经过多次尝试或者从仿生获得的灵感就可以一下子发明出来,电子计算机是无数人经过不断的探索才一步一步走到今天这种形态。我常常会想,现在的生活多么幸福啊,可以跳过那段没有网络,没有现在这种性能的计算机的“黑暗时代”。可是今天的计算机依然在发展,人工智能,区块链这些新技术层出不穷,说不定后人也觉得我们今天的生活是“黑暗的时代”呢。