唠唠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行。

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

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

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