读《巨婴国》

读完这本书已经有两个月了。其间恰好碰上过年,我回到山东老家,冬天冰冷的雪夜里,或是乍暖还寒的清晨,看着捧在光下发黄的屏幕,别有一番滋味。

巨婴国封面

第一次看到封面上奇形怪状的小人时,以为是一本漫画书。这本书读起来很轻松,思考起来又有点沉重。

书中有很多很有意思的观点,比如谈到“普遍缺席的父亲”,“把儿子视为权利争夺战筹码的母亲”。我对这些并不感冒,我的父母给了我足够的爱与自由。

我感触最深的一点,是讲到“中国式好人”。武志红说他自己就是一个典型的“中国式好人”,可能大部分人读到都会产生共鸣吧,毕竟“中国式的好人”占了绝大多数,因为有集体主义在压着。

我们总是有意无意地被灌输集体主义的思想,一切为了集体,要牺牲小我成就大我。这其实就是在压制自己,因为自私是人的天性。

从达尔文的进化论来看,生物的进化,靠的是物竞天择,而不同物种如何竞争?

我们在初中时就已经知道了,即使是单细胞生物,也懂得“趋利避害”的道理。当然,说懂得是过分了,它们不用懂得,竞争会把基因里存在“趋利避害”特性的物种筛选出来。

我身边有很多人,包括过去的我,都不好意思为自己争取利益,唯恐被人说闲话,或者自己道德上过不去。

书中也提到一个原因,如果我们把自己的某部分毁掉,比如我们不为自己争取利益,那我们就站在了道德的制高点,“我把本属于自己的一部分都不要了,你还怎么来指责我?”但越是打压自己的天性,我们的能量就会变得越萎缩。

不自私的人还容易碰到的一个问题是,述情障碍。我们接受的教育是,个人要服从集体,这导致我们很难感受到自己的感觉。自私的人是坏的,是可耻的。所以我们拒绝自己自私的部分,我们不想让自己的那部分阴暗被别人看到,被拒绝的部分就陷入了黑暗。

“中国式好人”不懂得拒绝。一切为了他人,导致自己最后活得很累。而且情况往往是,我们一边帮助他人,一边却又在抱怨,因为我们并不是因为感觉到了他们的困难,而是集体主义教导我们要帮助他人,不然自己就太自私了。

“中国式好人”没有自己的个性。个性是对集体的背叛,在一个以集体为生活单位的环境下,被集体排斥是很恐怖的一件事情。所以很多人为了融入集体,干脆泯灭了自己的个性。

自私是个好东西。只有能充分感知到自己的感受,我们才能对别人的处境感同身受,也才能真正发自内心地想去帮助他人。

自私对公司的管理一样有很大的好处。吴军在讲到公司管理时引入了一个数学概念,我觉得很好,叫做“利益最大化函数”,即我们将公司的发展与员工的利益绑定,员工为自己获得最大利益的同时,也可以为公司做出最大的产出。但是,如果一群人都是一群集体主义下催生的僵尸,没有感情,没有创意,打一鞭子就动一动,这样的人,做出的产出能有多少?

“老油条”应该就是这么来的吧?

读《编写可读代码的艺术》

感谢公司的图书角,让我免费读到了这样一本好书。

用kindle已经有一段时间了,也陆陆续续地在上面买了一些电子书。但慢慢地发现,还是读纸质书来得舒服,翻起来也方便。当然,最好是自己买的书,可圈可点,不喜欢的书还可以拿来擦屁股。

然后我就联想到了李敖的读书秘诀,给大家摘一段体会一下:

我李敖看的书很少会忘掉,什么原因呢?方法好。什么方法?心狠手辣。剪刀美工刀全部用到,把书给分尸掉了,就是切开了。这一页我需要,这一段我需要,我把它按类别分开来。那背面有用怎么办呢?把它影印出来,或者一开始就买两本书,把两本书都切开以后整理出来,把要看的部分分类留存。结果一本书看完了,这本书也被分尸掉了。这就是我的看书方法。

虽然有点偏激,但至少说明,读书要活读,要跟书互动,产生参与感,还要温故知新,常翻常新。纸质书可以做到,电子书总归不那么方便。

《编写可读代码的艺术》,一个书名就已经道出了它接下来的目标了。谈到艺术,大多时候是从微小处着眼,细细体味,而非一个hello world那么简单。有生活,有哲学,代码也该像一件精心雕琢的艺术品。

这本书的结构已经非常清晰了,再做总结总显啰嗦。下面是我读这本书时想到的,并非笔记。

想人所想

这个标题看起来一点都不酷,但是做起来甚至会感动到我们自己。我们不想看到队友因为看不懂代码而苦恼的样子,我们的用户已经疲惫不堪,我们不想让他们再承受着蹩脚的使用体验。

写代码和写文章是极其类似的,作者要站在读者的角度思考问题。

读者读到这个词时是否会理解它,会不会产生歧义,是否需要了解某个背景,这段话是不是多余,会不会太啰嗦。

作者要思考比读者多得多的东西,因为作者这个角色本身,是包含了读者角色的双重角色。

站在再高一点,从系统用户的视角来看,这个字段的描述用户是否能看懂,是否正确理解了它的用途,有没有必要填写,用户是否可直观地了解有哪些注意事项,是否知道在做什么,当用户看到这个提示时是否知道它在说什么,blahblahblah一大坨,慢慢体会。

我们即是作者,又是读者;即是开发者,又是维护者;即是产品,又是用户。

遵循惯例

遵循惯例可以看作是“想人所想”的一部分。

每个行业都有自己的惯例。伴随着行业的发展,大家在相互配合中,整理出了一些行之有效的通用规则。

理想的情况是,这些通用规则,本行业的人,大家耳熟能详。

既然大家耳熟能详,就很少存在误解,也可能更好地被理解。这无疑对读者更友好。

当然,惯例也有可能带来相反的效果。《编写可读代码的艺术》里提到过一个例子,get_xxx()按惯例来说就该是O(1)复杂度的函数,该书作者坚信这一点,结果该函数并非O(1)函数,导致作者在优化速度时吃了点苦头。

不过就像爱情一样,惯例是有的,你坚信,准没错,至少它能保证你在80%的情况下是正确的。这很值。

讲究卫生

一张干净的脸总是讨人喜欢的。

记得小时候家里不富裕,还穿过打补丁的裤子。老师教导我们,裤子破了打补丁很正常,但即使打了补丁,也要把裤子洗干净。一个漂亮的补丁,甚至会给一条旧裤子增色,就是这样。

在我的理解里,讲究卫生意味着整洁和干净。注意变量的出场顺序,把相关的代码或变量分块,都是一些好习惯。

好的代码就是要看上去赏心悦目。

可能有的人觉得,只要让系统运行起来就万事大吉了,很明显,这是不负责任。

我维护过一些系统,杂乱且无用的旧代码像战场上的残骸,任性的空格空行天马行空放荡不羁,拷贝的痕迹随处可见。这些都会加重读者的心理负担。

当然,我们也有能力写出这样的代码,所以,可以安静下来想想,怎么对待自己。

提升技能

书中提到一点我觉得很受用:偶尔地翻一下语言内置的和常用的第三方库。

维护代码需要很大成本,甚至我们的大部分时间都是在做维护的工作。以我之前做的“代码发布系统”为例,开发用了一个月,后期的维护已经达到8个月之久,而且还会持续下去。

而大部分第三方库都已经过严格测试,而且可以节约我们的维护成本。

有的程序员喜欢重造轮子,出于学习目的或现存库不能满足需求还好,如果是为技术而技术,可要在关键时刻提醒一下自己啰。

屏蔽细节

最后一点同样重要,帮助我们的读者隐藏不必要的细节。

函数的一个好处是,它把繁杂的上下文剥离开,让理解变得简单。比如,send_mail_html(mail_address, html)这个函数可以给指定邮箱地址发送一段html类型的文本,只要函数封装地够好,用户或主调函数无需关心它的实现细节。

这也是自下而上编码的好处,上层提供了比下层更高一级的操作接口,并为用户屏蔽下层的实现细节。从这个意义上来说,编程语言也是自下而上中的一层实现,语言的细节我们无需关心,只要它封装地够好且使用无歧义。

再往大了说,一个好的团队也应该有屏蔽彼此细节的能力,团队中的成员由不同的领域专家组成,大家的职责彼此正交。这时,如果你是一个虚拟化技术的专家,而我更擅长用系统来为大家提供帮助,如果我的系统需要申请一个临时环境,调用你的一个接口,我可以获得一个可用的ip,你怎么生成这个环境我不关心,我怎么用你也不用关心。再这时,如果我们要改变容器的管理方式,这对我们的用户是透明的,也就是说,他们甚至都不知道我们在做这一操作,用户只需关心他们所应关心的内容即可。

函数的使用亦如是,彼此一体却又边界清晰。

总结

编码如是,生活亦如是。

唠唠python(6) -- 事物的边界和内在

上一篇文章,我们讲到可以将一批操作打包成一个“功能”,从而屏蔽其内部实现的细节。

这篇文章,我们将对屏蔽细节做出进一步的努力,并让计算机的世界更符合我们的直观感受。

边界的重要性

昨天走在街上,看到一只动物,长得个绵羊样,体型却小得多。想起之前看到狗伪装成熊猫的样子,我就在心想,这可能也是一只演技派超强的狗。

在平日里,我们会有意无意地对看到的事物进行分类,这有很多好处。

首先,它能减少我们大脑的记忆量,屏蔽具体事物的一些不必要的细节。比如,我们现在肚子很饿,想吃点东西。我们发现路边有一个服装店,正常的人类,不用走进去就知道,里面没有我们急需的东西:食物。

其次,分类可以帮助我们更快地认识新事物。迎面奔来一只恶狠狠地狗,经验告诉我们,狗是会咬人的。所以,我们远远地躲开了它。狗喜欢骨头,喜欢吃屎,只要是一只狗,就可以对它的这些行为做出判断。

既然分类这么重要,如何进行分类呢?

凭直觉,可以。我们长这么大,没吃过猪肉也见过猪上树,看到长得像猪,八成就是猪了。

当然,凭直觉有时不那么灵。我就没凭直觉一眼看出那只迷你小绵羊其实是一只狗,而且直觉不方便数字化,我们无法直观地向计算机描述我们的直觉。

所以,除了直觉,我们应该还有其它的分类方法。

我们可以通过事物之间可识别的独特区别来区分事物,也就是通过事物之间的边界。比如,男人有鸡鸡,而女人没有,只要检查一下一个人有没有鸡鸡,就能判断他是个男人还是女人了。只要交给计算机这条规则,计算机也能很容易地判断一个人的性别了。当然,反之亦然,我们猥琐地猜想眼前的这个男人是有鸡鸡的。

划分边界

我们平时接触最多的是人,叫做“人类”。我们就以人为例,来想一下我们“人类”的一些事情。

首先,人有名字,有出生年月,有属于一个人具体的属性。

其次,我们知道,人可以说话,可以写字,可以吃喝拉撒。我们可以把这些看作一个人所具有的“功能”。这里的功能,其实就是我们上一篇所说的“功能”。比如,吃可能是由一系列的动作组成的,把食物塞到嘴里,咀嚼,下咽,blahblah…一系列的动作,我们把它们打包成一个功能,就叫做“吃”。

再次,当我们说“人类”的时候,实际上,我们在说的是一个概念,一个通过边界区分出来的分类,而并非特指老王。而当我们讨论老王的时候,我们就有了更具体的信息,我们不仅知道他有名字,还知道他叫“老王”,而且知道他出生于1962年(这让我们觉得“老”字合情合理)。很有意思的一点是,我们之前说的吃喝拉撒,老王全会,而且做起来跟其他人差别不大(不像名字那样千差万别)。老王就是人类具体化后的一个对象。

介绍隔壁老王

现在我们知道了更多隔壁老王的秘密,下面我们来做一些更有意义的事:把老王介绍给计算机。

听起来怪怪的,我们先来做点稍微正常点的事,把老王介绍给楼上老赵先。

“老赵,早啊。这位是我的隔壁,叫老王。60年代的人,跟你差不多大,以后有人陪你下象棋了。”

嗯,这个介绍还算正常。

我们试着再迈出一步,把老王介绍给计算机。

这时,有一个问题出现了。老王是个人,但计算机并不知道人是个什么东西。首先,我们需要跟计算机解释人是什么。我们不需要严格的人的定义,而只要梳理一下人自身有哪些信息,可以做哪些事情就可以了。

人有名字,有出生年月,可以说话,可以写字,可以吃喝拉撒。

我们只要把这些规则简单地转化成代码:

1
2
3
4
5
6
7
8
9
10
11
12
class Person:               # 定义人类
name = '' # 人类有名字
birthday = '' # 而且有生日

def speak(self): # 人类会说话
pass

def write(self): # 会写字
pass

def trivia(self): # 吃喝拉撒样样精通
pass

这里有几个新东西。class 意思是“定义一个类”。pass 是为了先占个位,我们在这里还不想涉及到 说话 的细节。

我们在这里看到了上一篇讲到的“功能”,这里的功能跟上一篇的“功能”并无二致。在执行这几个功能时,会传入一个 self,我们可以先暂时不予理会。

好了,现在计算机已经知道人类是个什么东西了,可以介绍老王了。

“老王是一个出生于1962年的老头。”

1
2
3
4
5
mr_wang = Person()              # 有一个人
mr_wang.name = '老王' # 他的名字叫“老王”
mr_wang.birthday = '1962-10-13' # 出生于1962年10月17日

print mr_wang.name # 打印老王的名字

这时,计算机就知道老王是怎么一回事了。尽管有点明知顾问,我们最后还是打印了一下老王的名字,计算机显示了一个“老王”。

想获取老王的个人信息,使用这种方式就可以了:mr_wang.name,很简单,对吧?

重新理解老王

上面的解释很容易懂,但是有几一点没有说清楚。mr_wang = Person() 意思是“有一个人”?

很明显这是一个动作,而不是一种状态(有一个人)的描述。

为了更清楚地了解 mr_wang = Person() 的实际意义,我们来回想一下钥匙的制作过程:如果我们的钥匙丢了,要去重新配一把,来到配钥匙的地方,会发现老师傅那里有很多没齿的钥匙。这时,老师傅会根据我们那把正常钥匙的齿,来打磨一把新的钥匙给我们。mr_wang = Person() 就是取没齿的钥匙的过程。打磨的过程就是将一个对象的特殊性添加到这个对象中的过程。

也就是说,我们先从人这个类里生成一个新的人(没有姓名、没有生日),然后把老王的数据打磨到这个人里(具体化)。

开口说话

我们之前说人类可以说话,但上面这个例子并不能讲话。我们来修改一下代码,让老王可以自报家门:

1
2
3
4
5
6
7
8
9
class Person():
...
def speak(self):
print "Hello, I'm", self.name
...

...

mr_wang.speak()

为了方便说明,这里只显示了关键代码。我们实现了之前 speak() 功能里的 pass 部分,使用了之前使用过的 print

这里有几点要说明:

  1. self 是我们之前留下的小尾巴,现在可以清晰地看到,self 实际上是实例的别称。当我们通过 mr_wang.speak() 来使用 speak() 功能时,self 就是我们亲爱的老王。
  2. 我们发现,使用一个对象的功能,和使用一个对象属性形式一下,只是多了一个功能所特有的 ()

小结

需要说明一点的是,计算机中的类很少是用来分类的,因为大部分情况下,我们已经提前知道一个对象是人还是狗了。它主要是用来划分边界,让我们意识到,这个对象有哪些信息,可以做什么。

小结不应该太多废话,我们把之前的代码整理一下,看懂即可,下载链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义人类
class Person:
name = ''
birthday = ''

def speak(self):
print "Hello, I'm", self.name

def write(self):
pass

def trivia(self):
pass

# 具体化一个人,并写入老王的信息
mr_wang = Person()
mr_wang.name = '老王'
mr_wang.birthday = '1962-10-13'

# 让老王开口说话
mr_wang.speak()

尾注

嗯,到现在为止,我们已经忽略了很多不需要知道的细节。

但可能在实操的过程中,我们的好奇心没有停下来,也或者因为小手一抖敲错个空格,而让问题变得非常复杂。我的建议是,保存好有错误的代码,然后重新来过,毕竟到现在为止,长点的代码也不过20行。

错误都非常有价值,多犯不同的错误会让我们更有经验。当你完成了整篇文章的内容,就可以回过头来重新查看之前的错误了。询问旁边的人,或者借助互联网,都是不错的选择。

在完成一个重要的里程碑后,我们会重点整理一下之前最有可能产生误解的点。

最后,毫无疑问的是,如果你发现某一句话没看明白,或者实操时老出问题,那一定是我出问题了。请给我一个改正的机会。

读《废都》

这是第二次读这本小说了,算是重温。

第一次读是在高中。那时去姨家玩,进了一个房间后,发现墙角有一个书框,密密麻麻地摆着好几排的书,想是堂哥平时慢慢积攒起来的。那时我并不喜欢阅读,就随便翻着看。翻到这本书时,看着书里的情节,面红耳赤,不能自已。

那时我十七八岁,正是情窦初开的年纪。

借这本书时,刚好堂哥不在,胆战心惊,怕被姨识破。我把书借回了家,仔细研读并温习着书中描写的场景。特别是读到“□□□”时(此处并非敏感词,原书中确为“□□□”),心中充满无限的想象。那份荷尔蒙急剧上升带来的怦然心动,一颗鲜红滚烫的心仿佛要脱离这个僵硬的躯壳。

所以想象力很重要。

就像冯唐讲他们在网络资源匮乏时,一个视频一传十十传百的刻录,最后模糊不清,也依然平静不下观看时的激动。最后乃至看到阳光下树影斑驳,下面都会不自觉地硬起来。

十年前,山东,盛夏的某个夜晚,我躲在被窝里,战战兢兢地抱着一本泛黄的纸书,虔诚得像一个圣徒,有作为生命最原始地憧憬与渴望。我读得兴奋,从外面远远地望过来,千家万户独一盏灯火通明,肯定更美。

一晃十年过去了,“十年生死两茫茫”。十年有可能是我生命的八分之一,五分之一,甚至是三分之一。

一席春梦了无痕。十年后,午后饭饱,阳光正好。人懒洋洋地坐在办公室的座椅上,旁若无人地拿着kindle,总觉得少了点什么。手里的这个版本,已经没有了当初的“□□□”,也不再有当时斟字酌句的心境。

不知再过他个十年二十年后,看到远处的灯火通明,是否还能一眼看出,那个曾经亲爱的自己。为之感动,为之神伤。

上课走神

我记得之前的每一次考试,心里都会非常紧张,惟恐整个考试被自己搞砸。甚至多次被这样的梦境吓醒,梦中自己竟睁不开眼看试题。

我慢慢地在这样的紧张中,缩小自己心理的活动范围。

如今又做了这样一个梦,说不上是算昨天还是今天,这篇文章是一篇跨夜文章。

梦里还是高中时候的同桌。

老师说:“你们先随便看看书吧。”像是地理书。

说完以后,我就开始自顾自地玩了。

过了一会,老师说,现在开始考查刚才让你们背诵的内容。

what???

what

逗我,啥时候让我们背诵了,不是说就随便看看吗?

更夸张的是,竟然是所有人挨个过去,他提问题我们回答。搞得更像是面试。

我的天,这是什么情况,我再一次慌了。

我前边的几个人挨个过去,都答的不错。

到我过去背诵,他先问我:“你觉得你背诵得怎么样?”

我说:“背得不是很好,尤其是后面。”

他一下子就怒了,怒了,说:“那还学什么了,我要把你开除”。

开除?我清晰地觉得这是在跟我说工作的事。

看到他在纸上写下开除两个字,我一下子就释然了。一个感觉是,你没权利开除我;第二个感觉是,开除我也不怕,又不是什么大不了的事。

醒来后,我觉得自己很开心。多年的恐惧已经解除了。

也或许,我真的有了一定能力,可以理直气壮的说,我不怕失去。

我觉得我真正要害怕的是生命的失去。失去生命,意味着将不再有努力的机会。

而考试,或者老师布置的任务,并没有可以让我们害怕的因素。

可接受的生活环境

租住在现在的这个地方已经有3个年头了,却几乎没怎么主动打扫一下。

并非因为不是自己的房子感觉没必要,也不是因为平时工作忙没时间。就一个字,“懒”。

今天破天荒的打扫了一下厨房的卫生,大到物品的摆放,小到边边脚脚。用毛刷刷墙角也是很快乐的,不得不说,人类的这项发明非常之伟大。

说起打扫的原因,却是出奇的简单:我住的这个房子,厨房靠窗。春天来了,想打开窗子透透气,但一打开,外面的人就能看到里面的情景。想到这个,我立马就有点心虚。窗子还是要开的,怎么办呢?打扫一下卫生呗。

可不可以开窗,取决于一个条件,我管它叫做“可接受的生活环境”。即,我能不能接受把我的生活环境暴露给别人看。

说到可接受,有两种很极端的情况:

  1. 虽然我房子脏,但是给别人看一下也无妨,毕竟我是活给自己的嘛。
  2. 这么脏的房子,怎么能给别人看到,除非打扫一下。

我觉这两种情况都合情合理,所谓“知行合一”,只要自己跟“自己”想法一致,就不会在心里产生矛盾。

我选择了2,而且给了自己一个很好的理由:嗯,虽然你小子看起来挺懒的,但是并没有懒的无可救药,你还是想努力改变自己的。

“可接受的生活环境”其实是与懒惰抗争的一个好方法,有时候我们就是需要这种本无大意义的想法来刺激一下。当然,意义多大看个人。

“可接受的xxx”可以用在很多方面,比如,我们平日在家希望自己是一个什么形象呢?我们有可能希望接受的是,别人看到的我们的私生活是积极的,我们热爱生活,我们利用自己的时间学习烹饪、读书、运动、听音乐。

但是,私生活本来就是不活在别人的眼里呀,不然该活得多累。所以实际的情况可能是,我们热爱不正经的生活,我们看黄色小说、打飞机、睡懒觉、臭鞋臭袜子衣服满屋。

如果不用打开自己的生活给别人看,又觉得脏点乱点自己也可以逍遥快活,管它呢!

唠唠python(5) -- 打包操作

这篇文章依然是承接自上一篇文章,来讲一讲上一章遗留下来的问题。

上一章我们讲到int(raw_input('Tell me the quantity: '))是让计算机帮我们从用户那里获取一个数字,现在我们来看看它是如何做的。

洗衣机的智慧

对,我们先来谈一谈洗衣机。

我现在住的这个地方有点小,担心买了洗衣机更显局促,所以一直没买。但这不妨碍我们谈洗衣机的智慧。

如果现在还没有出现洗衣机这么个东西,设想我们渴望有一个机器,它可以帮我们(1)自动加水,(2)清洗衣物,(3)自动甩干,(4)自动烘干。

这么一大批动作,当然每个动作分别需要一个机器也是可以的,但是每一个动作人为介入,势必会降低我们的效率。怎么办呢?

哈哈,其实办法我们已经知道了,但还是要假装吃惊一下。啊?居然还有机器可以集成上面说到的全部功能,而且只需要在执行第一步前按一个按钮,接下来的四步自动依次完成,帮我们屏蔽了之前的细节。

这样的例子有很多,人类的智慧大大促进了人们生活水平的提高。

洗衣机智慧在计算机语言中的妙用

刚刚我们谈了洗衣机的智慧,我们再进一步来谈一下,对我们来说,它到底意味着什么。

我们知道,光洗衣服这件小事,就涉及了非常多个工序。什么倒洗衣粉、搓洗衣服、拧干、晾晒,等了个等。像我们这种追求(zhi)生活(nan)品质(ai)的人,怎么能忍受得了这么变态的工序。

福音来了,现在你眼前的这个工具,只需要把衣物放进去,按下开关。二小时后,将衣物取出放进衣柜就可以万事大吉了。

为什么这么神奇,因为洗衣机帮我们把洗衣服的细节给屏蔽掉了。它把必须由我们来做的事(打开开关)暴露给我们(看得见的开关按钮),我们只需要打开开关,二小时后,它就会把结果(衣服洗完)给我们。

计算机语言也存在着类似的思想。

我们再拿之前提到的表格处理的例子,我们希望把表格中每个数字,按照某种规则处理完后写到其下面的表格,然后将处理后的表格保存到一个新文件。

假设我们有一台表格洗衣机,来达到上面的效果。我们假设这台表格洗衣机是洗衣机厂家生产的。我们不管厂家怎么制作出来的,也不管它怎么个洗法,我们的想法是,只要给这台表格洗衣机一个实际的数据表格(相当于洗衣机的开关)就可以了。它会把处理完后的表格给我们。

表格洗衣机仍然有点抽象,但是实际上,我们在前面已经看过两个例子了。一个是raw_input(),一个是int()

raw_input()int()

现在,我们可以来讲讲之前遇到的int(raw_input('Tell me the quantity: '))了。

这一坨长长的东西,可以等价到下面两句:

1
2
quantity_str = raw_input('Tell me the quantity: ')
quantity = int(quantity_str)

我们对洗衣机说:“帮我洗一件衣服”。然后塞给它一件衣服,打开开关,它就轰隆隆地开动了。

当计算机读到quantity_str = raw_input('Tell me the quantity: ')时,它跟用户说:“Tell me the quantity: ”,并将用户输入的文字写到quantity_str

quantity_str又是啥?我们可以把它想象成一个箱子,我们把计算机读到的用户输入的文字放到这个箱子里。

注意,这条语句是从右往左读的:“计算机显示Tell me the quantity,并把用户输入的文字放到quantity_str这个箱子里”。

现在我们知道了,raw_input()就是要计算机帮我们获得一段文字。具体怎么获得,我们不管,你(计算机)自己去想。

int()也是一样的东西,它的功能是将某段文字(如"1234")转换成一个整数。int(quantity_str)即,将我们quantity_str箱子里的文字转换成一个整数,并放到一个名为quantity的箱子。

为了更直观地感觉一下,我们来自己做一台”洗衣机”。

更好的规则计算过程

之前的例子,我们写了一段代码来实现通过规则计算结果:print (int(raw_input("Tell me the quantity: ")) + (10000 - 1)) / 10000 * 10000

这一行代码有几个问题:

  1. 首先,这一句非常长,而且里面有n个(),就算我们自己,也要用手一个括弧一个括弧地匹配。
  2. 其次,里面可能有多个中间结果,但我们没法查看中间结果的值。
  3. 再次,这段代码的自述性不好。也就是说,它不能表达自己在做什么。

我们根据这三点,再利用前面提到的技术,来重新组织一下我们的语言:

1
2
3
4
5
quantity_str = raw_input("Tell me the quantity: ")
quantity = int(quantity_str)
real_quantity = (quantity + (10000 - 1)) / 10000 * 10000

print real_quantity

看上去代码量更多了,真的值得吗?

审视一下我们上面提到的三个问题,新的代码每行不再有大量的括弧,我们可以在第二行后插入print quantity来查看quantity的内容,而且我们很清晰地看到了我们要计算机做的每一步。作为一个简单的脚本,新的代码并没有什么吸引力。但当我们考虑到我们要把它做成一个完整的系统,至少在最后的系统面前,它是值得的。

考虑当我们的规则非常复杂时,我们就会有一个更好的选择,即上面所谈的洗衣机的智慧:

1
2
3
4
5
6
7
8
def compute_real_quantity(quantity):
return (quantity + (10000 - 1)) / 10000 * 10000

quantity_str = raw_input("Tell me the quantity: ")
quantity = int(quantity_str)
real_quantity = compute_real_quantity(quantity)

print real_quantity

哇噻,更长了,哈哈。

我们在这里,定义了一个类似raw_input()的功能,叫做compute_real_quantity()。定义方式就像看到的一样简单。我们再来定义一个加法功能说明,如下:

1
2
3
4
5
def 功能名称(需要输入哪些变量):
return 结果

def add(m, n):
return m + n

这里有一个好处,如果compute_real_quantity()是别人写的,我们就不需要知道规则的细节了。假如这个规则是你来写,我只需要把我的待采购的数量给你,你帮我计算一下实际需要的数量就好了。具体细节,我不关心。

屏蔽细节让生活更轻松

我们之前说游戏手柄的输入也非常简单,原因就在于这方面的专家已经帮我们屏蔽了细节。获取手柄的输入,跟获取raw_input()的输入大同小异。

类似的,从一个表格文件中获取输入跟raw_input()差别不大。我们会使用这一篇文章中讲到的技术及之后的少量新技术,来从表格文件中读取表格数据,处理完后,使用类似print的功能,将处理结果输出到新文件(而不是神秘的黑色窗口)。

明天你是否会想起?

不止明天,我哪一天都不想起。睡觉。

唠唠python(4) -- 简化工作

胜兵先胜而后求战,败兵先战而后求胜。 – 孙子兵法

遇到问题

我的女朋友最近生病了,周三下了班就回了家,顺带着还把在公司的一部分工作带了回来。

等我回到家,她已经卧在床上睡一觉了。

过了一会她睡醒了,坐起来跟我说:“工作没做完,你帮我做一下吧。很简单,就是把这张表格里的数字,以10000为单位,上或下取整,填到它下面的格子里。”说罢,就又睡了过去。

怎么计算

我粗略地看了一眼,这是一个excel表格文件,就是下面的这张表格。

待处理表格

简单查看了一下excel的基础知识。这一个一个的格子,叫作“单元格”,哈哈,形象吧。暂时我们只需要这个概念。

这里面有黑色内容的单元格和红色内容的单元格。红色内容的单元格是计算上面的黑色单元格的内容(物料的待收数据)得到的结果。

我们的目标是,输入这个文件,然后让计算机自己经过一系列的计算过程,将表格中的还未计算过的数据按给定的规则计算,将计算结果写到下面一个单元格,并帮我们把处理后表格保存下来。

这个目标实在激动人心!我跟我女朋友确认过,以这次的数据量,如果人工来做,可能需要花费3-4个小时的时间。而对计算机来说,在一分钟内就可以完成,而且可以确保结果的准确性。需要的知识也浅显易懂,如果对相关知识熟练,完全可以在半小时内将我们想要做的事情清楚地告诉计算机。

当然,学习是循序渐进的。在这篇文章中,我们先把目标降低一些:告诉计算机一个待收数量,和一条规则(以10000为单位上取整),让它帮我们计算实际的代收数量。这是我们完成整个目标的第一步,这个技巧也完全可以用在其它类似的工作上。

获取输入

首先,我们需要告诉计算机待收数量。这当然很简单,我们在前面已经说过了。假设待收数量是1234:

1
2
3
>>> 1234
1234
>>>

但是,这样会显得很仓促:1234代表什么呢?我们现在当然知道,它就是我们要求值的数据。但当我们写入到一个文件时,如何把1234传进去呢?

显然,我们可以直接写到文件,比如:

1
print 1234

而此时,如果再重新输入一个数字时,我们就需要打开文件,然后把1234改为新的数字,保存文件,然后运行。修改代码文件很繁琐,而且是很危险的,这还只是一行简单的代码,想想,如果一个代码文件有上百行,我们如何再找到这个数值的位置?会不会小手一抖,把其它行的数据也修改了而自己并没有意识到?繁琐的操作也必然会降低我们的效率。

这时,我们就自然有这么一个需求。

计算机:您好,我最亲爱的某某某,您的待收数量是多少,我来为您计算实际待收数量。

亲爱的某某某:嗯,1234。

计算机:您实际需要待收的数量是10000。

嗯,这就是人们所说的交互,其实就是让人跟计算机的沟通像人与人的沟通一样顺畅。

这时,我们需要知道这么一个东西:

1
2
3
4
>>> int(raw_input("请输入您的待收数量:"))
请输入您的待收数量:1234
1234
>>>

注意,里面的第一个1234是我们自己输入的,后面还跟了一个回车。

int(raw_input("请输入您的待收数量:")),这么大一坨?没关系,先死记下,在之后的文章我们会进一步了解。我们只需要知道,它让计算机询问我们待收数量。

如何计算取整后的值

我们现在用的python是个死脑筋。为什么这么说呢?如果你使用的python是之前文章中链接的python或是系统默认安装的,试试下面这个:

1
2
3
>>> 5 / 2
2
>>>

我们在学校学到的可是5 ÷ 2 = 2.5,现在却得到个2。谁的问题?

语言的问题。语言是有局限的,而当我们输入5 / 2时,python解释给计算机说:“请帮我们计算一下5 ÷ 2的值,取整就可以了,小数点我们就不要了。

语言是在发展的,文章中用的python版本是python2,此时,默认情况下,整数除法的结果是整数,而且是下取整。

现在回到我们的问题:给一个数字,以10000为单位,上取整或下取整。我们假设是上取整,想想应该怎么做呢?

我们把单位缩小,先来看一下以3为单位上取整,我们来取一批数值观察一下。

我们来分别观察一下 待收数量实收数量 的值和它们之间的相互关系格式:

  • 待收数量:1,实收数量:3,待收数量 ÷ 3后下取整:0
  • 待收数量:2,实收数量:3,待收数量 ÷ 3后下取整:0
  • 待收数量:3,实收数量:3,待收数量 ÷ 3后下取整:1
  • 待收数量:4,实收数量:6,待收数量 ÷ 3后下取整:1
  • 待收数量:5,实收数量:6,待收数量 ÷ 3后下取整:1
  • 待收数量:6,实收数量:6,待收数量 ÷ 3后下取整:2
  • 待收数量:7,实收数量:9,待收数量 ÷ 3后下取整:2
  • 待收数量:8,实收数量:9,待收数量 ÷ 3后下取整:2
  • 待收数量:9,实收数量:9,待收数量 ÷ 3后下取整:3

我们发现,待收数量 ÷ 3后下取整 的值会随着 实收数量 增长,但并不完全一致,而是相差 2。如果在 ÷ 3 前先将 待收数量 +2,则结果刚好变成了3倍的关系。如下:

  • 待收数量:1,实收数量:3,(待收数量 + 2) ÷ 3后下取整:1
  • 待收数量:2,实收数量:3,(待收数量 + 2) ÷ 3后下取整:1
  • 待收数量:3,实收数量:3,(待收数量 + 2) ÷ 3后下取整:1
  • 待收数量:4,实收数量:6,(待收数量 + 2) ÷ 3后下取整:2
  • 待收数量:5,实收数量:6,(待收数量 + 2) ÷ 3后下取整:2
  • 待收数量:6,实收数量:6,(待收数量 + 2) ÷ 3后下取整:2
  • 待收数量:7,实收数量:9,(待收数量 + 2) ÷ 3后下取整:3
  • 待收数量:8,实收数量:9,(待收数量 + 2) ÷ 3后下取整:3
  • 待收数量:9,实收数量:9,(待收数量 + 2) ÷ 3后下取整:3

也就是说,实收数量 刚好是 (待收数量 + 2) ÷ 3后下取整的3倍,其中的 2 实际上就是由 3 - 1 得来的。我们用数学公式整理一下现在的这个想法:

1
2
3
实收数量
= (待收数量 + 2) / 3 * 3
= (待收数量 + (3 - 1)) / 3 * 3

注意,其中的 / 符号即我们python语言里的除法,它是下取整的。

类比到10000时的情形,我们的公式会变成下面这个样子:

1
实收数量 = (待收数量 + (10000 - 1)) / 3 * 3

实际上,如果我们可以让计算机整数相除时上取整(而不是下取整),公式就会变得异常简单:n 上取整除以 10000 * 10000,在我们学习了后面的更多知识后就可以做到。现在这个公式(n + (10000 - 1)) / 10000 * 10000虽繁琐,但已是我们通过自己仅有的一点计算机知识所能学到的了。

马儿跑起来

下面,我们将之前的分析总结到一个文本文件里:

1
print 10000 * (n + (10000 - 1)) / 10000

将n替换为提示用户输入待收数量:

1
print (int(raw_input("Tell me the quantity: ")) + (10000 - 1)) / 10000 * 10000

将以上内容保存到a.py,然后输入python a.py并回车,试试看:

1
2
3
$ python a.py
Tell me the quantity: 1234
10000

嗯,不错,可以。

一点遗憾

这是我们迈出的很重要的一点,如果你跟下来并且看懂了,这说明我们已经掌握了把自己的思想描述给计算机的技巧。

但这还不完美,离我们的目标自动处理数据并保存结果还是有一定距离的。多远的距离呢,嗯,可能是4-5篇文章吧。不算远,对吧,假定一篇文章40分钟,加起来也就是3-4个小时的样子。想想我们前面提到的,这些数据手工处理都要3-4个小时,太他妈赚了。

当然,3-4个小时是不够的,我们还需要一些时间消化和遗忘。不过,不管怎么算,这都是人生中很成功的一笔交易。

这篇文章中,还有很多有趣的东西被我一带而过了,留到下一篇我们再一起聊聊吧。

已经晚上23了,洗洗睡了。明天又是崭新的一天,啊哈哈。

我的世界(1) - 第一个夜晚

记得那是我来到这个世界的第一天。

我站在呼啸的风中有点瑟瑟,低头时才发现,自己的身体就这样裸露地暴露在空气中。

新奇伴随着陌生,举目四望,空无一人。目所能及之处,低矮的橡树,崎岖不平的草地,隐约能看到几个水塘。我抬起头,看到很远的地方有一个亮白色的圆点。

后来过了很久,我从书上看到,我其他的同类们叫它“日”。“日”,听起来动感十足,看上去也像是在跳动一般。

我四处游荡,想快些了解这个陌生的世界。

我来到水边,看到了朦朦胧胧的倒映的自己:一头杂乱的棕色长发,多毛的灰色脸面。

一切都是新的。一上午走下来,肚子开始咕噜噜地叫,步子也慢慢地沉重下来,眼前的景物越来越不真实。

就在这时,我发现不远处有一只白色的动物在走动,内心有点欣喜,又有点恐惧。

我必须向前,饥饿战胜了一切。

当我慢慢靠近它时,它并没有跑。这给了我很大的信心,我感觉眼角有点湿润。

为了防止它攻击本就虚弱的我,我决定忍着饥饿,在它身边观察上一小会。它长得跟我有点不一样,长着一身绒绒的白色的毛,有着像我一样的灰色的脸,也有像我一样的四肢,但是四肢都是立在地上的。偶尔会“咩咩”地叫几声。

它缓缓的走动,“咩咩”的叫声,让我觉得它不太可能会攻击我。此刻我必须觉得它不会攻击我。

我抄起身边的一根木棒,快步走向前去,将木棒重重地拍在了白色动物的脑袋上。它“咩”地一声大叫,疯狂地朝远处跑去。我也疯狂地追奔过去,又重重地加了一棒。这次它没有再发出“咩咩”的叫声,“噗通”一声倒在了地上。

一棒子,拖走

我开始不顾一切地胡吃乱啃一番,以夺回被饥饿渐渐控制的身体。而此刻我所有的行动,都是出于最原始的本能。

吃过肉以后,身体渐渐地恢复了一些。

许久之后,我知道这是一种叫做“绵羊”的动物。正如其名,这真是一种温柔的动物。

这时,四周开始渐渐地暗了下来。“日”也已变成了暗红色。一阵末名的恐惧开始朝我袭来。

我猜想,黑夜将会彻底摧毁我的视力,这眼前的一切都将会变成未知,我将会陷入永无止境的黑暗。

恐惧一下子包围了我,而且我对这种处境无能为力。无力感在慢慢侵入我的身体,此刻,我必须要找到暂时躲避这种未知恐惧的地方。

我四下环顾一周,发现了一个山洞。我来到这个山洞前,向里望去,没有发现其它生物。我必须在视力完全丧失前将洞口堵住,以确保没有未知的生物闯进来。

我匆忙地捡起些石头,趁着还有一点点光亮,快速地将洞口堵住,只留下一个小口,让我可以知道外面的情况,以免记不得山洞的出口,最后可能把自己也遗忘在山洞里。

我蜷缩在狭小的山洞里,突然听到有什么东西碰到草地的“簌簌”声,过了一会,看到一个绿色的怪物,吓得我喘气都缓慢了下来。幸运的是,它没有看到山洞中的我。当看着它越走越远时,我被自己感动了。

如果夜晚来临时,有哪怕一丝光,像太阳那样,可以照亮我的四周,该有多好?!

山洞里一片漆黑,只能听到我自己的心跳。抬起头时,从留下的小口处向上望去,我惊喜地发现了头顶上方,遥远的地方闪着一些明亮的小东西。后来我惊喜地知道,那叫做“星空”。

如果我离它们足够近,是不是就可以感受到光亮。

我决定,如果能活下来,一定要想办法去到那个明亮的小东西那里。它们给了我生的希望。

明亮的小东西成群结队地缓慢移动,在暗蓝色的背景下,承载了我无限的想像。一切也在不知不觉间发生了微妙地变化,时间缓慢移动,暗蓝的背景在慢慢地变得明亮,最后仿佛像我刚来时的样子。

我鼓起勇气,打算到外面看看情况。我缓慢地挪开堆在洞口的几块石头,朝外面望去。四下都是我刚来到这里的情景。远处一片绯红,“日”出现了,并且缓缓升起。

世界再一次在我的眼中模糊了。

唠唠python(3) -- 保存计算过程

昨天的文章,我们学会了跟计算机进行简单的语言交流和用计算机来做简单的算术。我们发现,计算机用来做简单的算术还是很方便的。但不知你是否注意到了,每次当我们退出翻译器时,我们之前的数据就消失了。

消失比错误更可怕,它代表着我们已不能确定对错,而且就算错了,错误的根源也无从找起。

多可怕!

奇怪的翻译器

在上一篇文章里,我们通过实验,得知,在python翻译器里输入"hello",翻译器给我们回复了一个'hello'。有的朋友可能已经注意到了,输出的hello两边有一对'

1
2
3
>>> "hello"
'hello'
>>>

不对呀,我们输入的是",结果里带的却是'?!

先别慌,我们说在python里,一句许两边要带",实际上,'"在python里面功能是一样的。没错,"hello"'hello'并没有什么本质的区别。

那现在问题来了:

  1. 为什么python的输出里要带一对'呢?
  2. 我就是不想看到这个',怎么办?

跟计算机打招呼做算术题是一码事

没错,在python翻译器看来,在对待这两者时,处理方式是一样的。

想想我们在做1+1时,翻译器做了什么?它把我们输入的文字翻译给计算机,并将结果显示给我们。

那当我们输入"hello"时,翻译器又做了什么?它把我们输入的文字翻译给计算机,并将结果显示给我们。

看到了吗,这两个答案其实是一致的。不同之处在于值本身,一个值是一句话,而另一个值是一个数字。

试想,如果把"hello"换成"1",而翻译器显示了一个1,我们就不知道这是一句话还是一个数字了。所以,结果,跟我们的猜想一致。

1
2
3
>>> "1"
'1'
>>>

现在问题1的答案已经很清晰了,为什么python的输出里要带一对'呢?这代表求值的结果是一句话,而非一个数字或者其它什么。

主动地表达我们的意图

对,是时候表达我们的想法了。

之前,我们是把python翻译器当作一个计算器,对一句话的计算也好,对一个算术表达式的计算也罢。

对一句话求值,所得的结果是一句话。为了表明它是一句话,结果的两边加了一对'

现在,我们要对计算机提出更高的要求。我们不需要计算机帮我们对一句话求值,我们想直接把一句话显示到屏幕上。这也就是问题2的答案,显示意味着我们不再需要知道这是一句话或一个数字,而是想知道这个值表达的是什么。"1"1你都给我显示1就好了,我知道是什么意思。

1
2
3
>>> print "hello"
hello
>>>

我们告诉翻译器,帮我们显示一句话"hello",计算机显示给我们一个没有"hello

需要注意的是,这里的print "hello"仍然会求值并将值显示出来。但因为这里的值是空的,所以我们只看到了hello

hello是求值过程中产生的输出,而求值没有结果

保存我们的计算过程

前面说,我们有时需要查错,所以需要保存计算过程

保存计算过程还有一个更重要的意义,因为我们的目标,是让计算机帮我们更高效的工作,如果不能重复的使用我们之前使用过的计算过程,意味着工作不仅没有高效起来,反而更加低效了。

所以保存我们的计算过程,就显得十分之必要。

从前面我们输入过的代码来看(对,我们已经是在写代码了),代码的表现形式非常简单,都是文字,或者说文本。也就是说,只要我们把以上的代码记录到文本文件里面就完成了保存计算过程的目标。

保存计算过程就是这么简单。

不过这里还有几点需要补充说明一下:

  1. 对文件名的要求:文件名要以.py结尾,以表明文件里存放着用python语言写成的”小诗”或”文章”。如果你不知道什么名字好听,a.py或者b.py都是不错的选择。
  2. 执行时要注意,执行的结果,更遵循主动地表达我们的意图,而不是直接求值并显示值。这里可能不太好理解,在python翻译器里求值时,翻译器会直接显示求值结果,但是,当直接执行保存在文件里的代码时,求值结果不会再直接显示,而是需要我们使用前面提到的print来告诉计算机显示。

重复使用计算过程

我们先来实践一下保存我们的计算过程

还记得上一篇文章中提到的那个神密的黑色窗口吗。打开黑色窗口后,输入notepad a.py(windows)或nano a.py(其它系统)并回车,这会创建一个新的名为a.py的文件。打开文件后输入以下内容并保存。这样,我们的计算过程就被保存到了a.py这个文件。

1
print "hello"

接下来,我们就可以使用这个计算过程了。

在黑色窗口内输入python a.py并回车,即可看到类似我们之前的输出了。

1
2
C:\> python a.py
hello

这个命令跟之前请出翻译时使用的命令,唯一区别在于这里多了一个文件名。

只是显示一个"hello",未免显得单调乏味。但是实际上,这代表着我们已经可以完成一个简单系统hello的从开发到运行的整个过程了。

当然,这还远远不够。我们需要这个系统能接收用户的输入(有可能是按钮点击、文字输入或者游戏手柄按键输入等),并可以通过复杂的计算过程,最后得到我们想要的结果(有可能是一个处理过的excel文件,或者一张图片,一封邮件等)。

当然,丰富多样的输入,复杂计算,不同载体的输出,也都是十分简单的。想想我们输出hello的过程吧,与此大同小异,只是对一些新鲜事物更熟悉的过程。

下一篇,我们就来利用已经学过的知识,与计算机这个古板的老朋友一起合作,来抽象一部分我们繁琐的工作。