一点关于 iter 的历史

本文是另两篇文章的预备。想必很多人都知道 iterator 这个概念,也知道 Python 里有一个 __iter__ 方法。 这里想讲一些关于 iterator 的历史。

Iterator 这个概念在很多编程语言里都有,Python 引入 iterator 是在 2.2 版本,也就是 2001 年的时候。 Iterator 是用来干嘛的,维基百科已经写得很清楚:

In computer programming, an iterator is an object that enables a programmer to traverse a container, particularly lists.

那么问题来了,在引入 iterator 之前,Python 是怎么遍历,比如说,list 或者 dict 的呢?

先来看 list。实际上是依赖于 __getitem__ 方法。估计大部分人对这个方法并不熟悉,其实很简单:

>>> [1,2,3].__getitem__(1)
2

给一个 index,返回相应的元素,相当于调用 [1,2,3][1]
于是,只要定义了这一个方法,我们就可以遍历自定义的类了:

class MyClass:
    def __getitem__(self, index):
        if index > 3:
            raise IndexError("that's enough!")

        return index

for name in MyClass():
    print(name)

输出 shell 0 1 2 3 看见 IndexErrorfor in 就知道遍历到头了,所以并不会报错。实际上 2.2 之前的 for in 都是调用 __getitem__

再看 dict。在 2.2 之前,我们并不能 for x in some_dict 因为 dict 并没有定义 __getitem__。 想遍历 dict 的话只能这样: python for key in some_dict.keys(): print(some_dict[key]) 显然这样性能不会好,因为要先生成一个包含所有 key 的 list。

Python2.2 引入了 iterator,相关文档参见 What’s New in Python 2.2 - PEP 234: Iterators。总结起来有以下改进:

  1. for x in C 会默认调用 iter(C),如果 C 没有定义 __iter__ 方法,会使用 __getitem__
  2. dict(以及其它一些内置类型)现在实现了 __iter__ 方法,所以可以 for x in some_dict 了。这样遍历 dict 比原来快很多,因为不需要生成一个 key list;
  3. for in 默认调用 iter() 的好处在于,用户可以对遍历过程应该返回什么,如何返回有更强的控制。另一方面就是拓宽了 for in 的使用范围,因为并不是所有我们希望遍历的 object 都拥有有意义的 index。比如之前 MyClass 的例子,index 就没有含义,所以用 __getitem__ 实现并不合适。


这篇文章主要是讲历史。下一篇文章详细讲 iterator 要怎么用,最后一篇对三种实现遍历的方式进行比较。

参考资料:
https://en.wikipedia.org/wiki/Iterator
http://effbot.org/zone/python-for-statement.htm
https://docs.python.org/3/whatsnew/2.2.html#pep-234-iterators

开始学 Go 了

虽然早就想开始学,然而一直没有时间。正好之后也不面试了,全力准备 team match,正是开始学 Go 的好时机。

目前在跟着无闻的《Go编程基础》学。感觉看视频还是最快的。

学了变量的声明&定义的写法之后,我感觉 Go 搞出三种不同的写法完全就是为了迎合不同语言的开发者。

原来是写 C/C++/Java 的,你还是可以像以前那样明确地声明类型 var a string = "ss",也可以先声明再定义。

原来是写 Js 的,那还犹豫啥,var a = "ss" 走起。

原来是写 Python/Ruby 等动态语言的,就加个冒号吧:a := "ss"

于是大家都觉得,嗯?Go 好像和我之前写的语言挺像的,不错。

不过也许这么设计是有其它原因的吧。

9.16

又多学了一些语法,发现 Go 的设计特别有意思,经常让我想笑(并非贬义)。这些设计我把它总结为:消除最佳实践,消除争辩。什么意思?在对任何一种编程语言的讨论中,程序员往往会为完成同一件事的程序到底怎么写是最好的而争论不休。最著名且吵得最厉害的例子自然要数“大括号到底放在函数定义的同一行还是下一行”。这种争议也不能说完全没有价值,但是很浪费时间。Go 的设计者说,让我们从最开始设计语言的时候就消除这些争议:

  • a++/a-- 只能单独作为一行,再也不怕谭浩强出的那些奇奇怪怪的问题了

  • switch 语句默认不 fallthrough,防止一些自以为很聪明的人到处宣扬自己多么会利用 fallthrough 特性

  • 用首字母大小写决定可见性,避免每个程序员自己发明一套可见性命名规则

  • 大括号规定必须放在同一行。看到这个我真的笑了,那些鼓吹新开一行好的程序员要尴尬死了,不过就让他们继续写 C++/Java 吧

以上几个算是为了消除争议而做的设计。还有为了消除最佳实践的设计:

  • break LABEL/continue LABEL 简直不要太爽
    看看这个问题 How to break out of multiple loops in Python?,最高票答案提出了一种最佳实践:把 nested loop 部分单独写成函数,然后把 returnbreak 用。我觉得这个方法相当丑陋,怎么可能 nested loop 部分的逻辑正好适合抽象成函数?更逗的是,下面有人提到:

    PEP 3136 proposes labeled break/continue. Guido rejected it because "code so complicated to require this feature is very rare". The PEP does mention some workarounds, though (such as the exception technique), while Guido feels refactoring to use return will be simpler in most cases.

    虽然我支持 Guido 强推 Python3,但不得不承认老爹有时候是比较顽固,因为我实在不觉得这是 “very rare” 的情况。

  • 数组作为值而不是引用传递
    把列表传入函数并对其进行修改是常见需求。 如果是 Java/Python 这种默认传引用的语言,显然既可以在原处修改列表,也可以返回新列表。于是有人提出最佳实践,说应该返回新的列表而不是在原地修改。Go 这种新语言居然不是传引用而是像 C 一样默认传值,我认为是刻意设计的(当然也可能是我想多了),即是说,我们不需要讨论最佳实践,你们老实地返回新列表就行了。

9.25

现在又学了 slice, map 和函数。在理解 panicrecover 机制上花了许多时间,找了几个教程包括官方文档都没讲清楚。最后终于找到一个:Understanding Defer, Panic and Recover。现在总算是明白了。里面有一个比喻非常好:调用 panic 就好像推倒一块多米诺骨牌,函数会逐层停止执行,直到 main 函数,然后程序崩溃。而 recover 就好像是把其中一块骨牌抽走,把程序崩溃的过程在 recover 处停止。所以 recover 之后的代码还能够正常执行。

这篇文章里还讲了使用 panic, defer, recover 的一些最佳实践,比如为了让 main 函数可以接收到 panic errordefer 要传 &err,然后在 defer 函数里给 *errrecover 接收到的 Error。看来 Go 还是有最佳实践的,只不过需要应用的东西不同了。

两部让我难受的动画

要说的正是 P.A. Works 的 《Charlotte》

看完 11 话大家纷纷表示要给麻枝准寄刀片。一般开虐的时候都会出现这种情况,我早就习惯了。我并不想寄刀片,因为这个剧情在我看来也就一般虐而已,而且说实话虐得比较生硬,很难调动我的“悲伤”的情感。然而 11 话的剧情确实让我看得很难受,并且勾起了我看《此时此刻的我》时不好的回忆。

个人品味这篇文章里我曾经写道,想弄死《此时此刻的我》里面的男主。原因是什么呢?因为男主整部剧中一直在犯蠢,而且经常拉着别人一起犯蠢,结果本来可以有好结果的人也被他害死了。真的,让我看这种剧情,就好像让我听两块泡沫在一起反复摩擦一样,完全是对身心的折磨。夏洛特简直如出一辙。男主一身绝技却怂的不行,关键怂得太突然了:本以为他听到友利被抓会直接发狂,结果直接怂得不敢去,我惊得下巴掉地上。好,罢了,就当男主是这个性格吧,但是他哥又是怎么回事?作为一个经历了无数次轮回的人,不要求你变得像胸针一样能欺骗世界,至少达到正常成年人的水平吧。首先是无预案这点让人无语,你既然早就担心国外能力者来日本,他们真来了的时候怎么会被水淹没不知所措?事先都没准备一下的?好,集合几个头头开会,决定让男主一个人去。恩,我觉得这个方案还是挺合理的,但是能不能考虑一下执行者的能力?如果是男主在另一个世界的兄弟鲁鲁修去,那妥妥的团灭对方,但是男主听到要让自己去吓得都快把基地拆了,居然还坚持让男主去,哥哥脑子里是进水了?还有就是对待叛徒的这种圣母心态我简直是无法理解了,叛徒一家活了,你们都死了很开心是吧。而且哥哥一开始明知对方有家人还让他加入,自己埋了祸根。唉,总之犯蠢行为太多,不甚枚举。

麻枝准参与的动画我基本都没看过,唯一看过的一部《神枪少女》里麻枝准只负责音乐,难怪剧情不错。既然是讨论“虐”,我就来列几部真的有虐到我的动画:

《神枪少女》第一部&漫画
神枪第一部是除作者相田裕之外公认的神作。我之前在豆瓣写过影评。漫画更好。花一样的少女,无可奈何的命运,相信看过的人很难不动容。

《甲贺忍法帖》
甲贺也是我在《个人品味》里就提过的作品。甲贺真 TM 太催泪了,又是命运的无可奈何。死法的惨烈程度基本也无人出其右。

《双恋 Alternative》
虽然结尾是 HE,但是中间很虐。少了身边最重要的人的空荡房间,这个意向是极好的。

《真实之泪》
剧情虽然没什么新鲜,但是胜在描写,不然怎么起党争呢?

《K-ON!》
特指最后唱相遇天使那段,看过的都懂。

有一些网上别人觉得虐的比如妖精的旋律、日在校园我倒是没觉得虐,然后很多据说虐的动画因为不合我口味也没有去看,比如小圆、未闻花名、白色相簿、秒5这种。总的来说,我看的动画不多,不过什么是虐还是能体会到的。

说回《Charlotte》。我认为你如果想让观众觉得“虐”,剧情发展就一定要符合逻辑,动画中的人物行为也要合逻辑。有人看到这估计要说,动画要讲什么逻辑啊,这么较真你居然还能看得下去动画。“动画可以不讲逻辑”这显然是一个错误的认识,纵观佳作,没有一个是不讲逻辑的。电波作并不是不讲逻辑,而是有它自己的一套表现手法,像我推崇备至的《FLCL》,剧情天马行空,但是没有一丝一毫不讲逻辑的地方。里面的每个人物的行动都是符合自己身份的,而女主看似不合理的举动在最后发现都是巨大计划的一部分。所以为什么虐心的作品,不论是动画漫画还是小说,很多都包含“命运的捉弄”这一元素,因为命运无常,所以被命运捉弄是符合逻辑的。夏洛特呢?我前面分析过了,如果说男主怂还情有可原,那么男主哥哥的表现就是完全不符合逻辑的。麻枝准如果想圆这个坑,可以这么说,男主的哥哥得了一种病,在动画 11 集的时候会出现智商突然下线的症状,这样就讲得通了。

最后我还想对比下《Charlotte》和《命运石之门》。两部都看过的人自然知道为啥要对比它们,所以这点不多说。我们看看石头门里是怎么描写轮回救人的:胸针从一开始信心满满相信一定能救嘟嘟噜到最后对嘟嘟噜的死亡已经麻木,这个转变的过程描写得相当细致。胸针最后那种疲惫的表情,那种生无所恋的样子,完全让我沉浸在了这个氛围当中,即使我一点都不喜欢嘟嘟噜这个角色。夏洛特是这么描写的:你们看,哥哥轮回了几次,他做了这些这些事,最后建立了研究所,没了。观众们:哦。再看看石头门是怎么描写穿越者和他亲近的人的相遇,说的就是胸针和助手的相遇。助手一开始是完全不信的,后来胸针说出"my fork"才让助手相信,到最后胸针每次 time leap 都只和助手说,也只有助手能相信他,这段简直不能再赞。什么叫无数世界线身边只有一个人,就是这样。夏洛特呢?男主和友利说,你信吗?友利说,我信。然后没了。Fuck!你们设计出这样一对角色就这么一笔带过真的好么??真的好么??

当然,《Charlotte》受集数限制没法展开太多,但是这个锅,麻枝准还是要背。明知剧情紧张,前面搞那么多单元剧干嘛?我曾经以为,那些单元剧里的线都是要收回来的,这些人后来会再出现。但现在我可以保证在麻枝准的安排里他们就是纯路人,因为《Charlotte》就这个水平,什么伏笔,都是你们想多了啦。


top