# 练习42.对象、类、以及从属关系
有一个重要的概念你需要弄明白,那就是“类(class)”和“对象(object)”的区别。问题在于,class 和 object 并没有真正的不同。它们其实是同样的东西,只是在不同的时间名字不同罢了。我用禅语来解释一下吧:
~~~
鱼和三文鱼有什么区别?
~~~
这个问题有没有让你有点晕呢?说真的,坐下来想一分钟。我的意思是说,鱼和三文鱼是不一样,不过它们其实也是一样的是不是?三文鱼是鱼的一种,所以说没什么不同,不过三文鱼又有些特别,它和别的种类的鱼的确不一样,比如三文鱼和大比目鱼就不一样。所以三文鱼和鱼既相同又不同。怪了。
这个问题让人晕的原因是大部分人不会这样去思考问题,其实每个人都懂这一点,你无须去思考鱼和三文鱼的区别,因为你知道它们之间的关系。你知道三文鱼是鱼的一种,而且鱼还有别的种类,根本就没必要去思考这类问题。
让我们更进一步,假设你有一只水桶,里边有三条三文鱼。假设你的好人卡多到没地方用,于是你给它们分别取名叫Frank, Joe,和Mary。现在想想这个问题:
~~~
Mary和三文鱼有什么区别?
~~~
这个问题一样的奇怪,但比起鱼和三文鱼的问题来还好点。你知道Mary是一条三文鱼,所以他并没什么不同,他只是三文鱼的一个“实例(instance)”。Frank和Joe一样也是三文鱼的实例。我的意思是说,它们是由三文鱼创建出来的,而且代表着和三文鱼一样的属性。
所以我们的思维方式是(你可能会有点不习惯):鱼是一个“类(class)”,三文鱼是一个“类(class)”,而Mary是一个“对象(object)”。仔细想想,然后我再一点一点慢慢解释给你。
鱼是一个“类”,表示它不是一个真正的东西,而是一个用来描述具有同类属性的实例的概括性词汇。 你有鳍?你有鳔?你住在水里?好吧那你就是一条鱼。
后来河蟹养殖专家路过,看到你的水桶,于是告诉你:“小伙子,你这些鱼是三文鱼。” 并且专家还定义了一个新的叫做“三文鱼”的“类”,而这个“类”又有它特定的属性。长鼻子?浅红色的肉?生活在海洋里?吃起来味道还可以?那你就是一条三文鱼。
最后一个厨师过来了,他跟专家说:“非也非也,你看到的是三文鱼,我看到的是Mary,而且我要把Mary和剁椒配一起做一道小菜。”于是你就有了一只叫做Mary的三文鱼的“实例(instance)”(三文鱼也是鱼的一个“实例”),并且你使用了它,这样它就是一个“对象(object)”。
这会你应该了解了:Mary是三文鱼的成员,而三文鱼又是鱼的成员。这里的关系式:对象属于某个类,而某个类又属于另一个类。
## 写成代码是什么样子
这个概念有点绕,不过实话说,你只要在创建和使用 class 的时候操心一下就可以了。我来给你两个区分 Class 和 Object 的小技巧。
首先针对类和对象,你需要学会两个说法,“is-a(是啥)”和“has-a(有啥)”。“是啥”要用在谈论“两者以类的关系互相关联”的时候,而“有啥”要用在“两者无共同点,仅是互为参照”的时候。
接下来,通读这段代码,将每一个注释为`##`?? 的位置标明他是“is-a”还是“has-a”的关系,并讲明白这个关系是什么。在代码的开始我还举了几个例子,所以你只要写剩下的就可以了。
记住,“是啥”指的是鱼和三文鱼的关系,而“有啥”指的是三文鱼和鳃的关系。
~~~
## Animal is-a object (yes, sort of confusing) look at the extra credit
class Animal(object):
pass
## ??
class Dog(Animal):
def __init__(self, name):
## ??
self.name = name
## ??
class Cat(Animal):
def __init__(self, name):
## ??
self.name = name
## ??
class Person(object):
def __init__(self, name):
## ??
self.name = name
## Person has-a pet of some kind
self.pet = None
## ??
class Employee(Person):
def __init__(self, name, salary):
## ?? hmm what is this strange magic?
super(Employee, self).__init__(name)
## ??
self.salary = salary
## ??
class Fish(object):
pass
## ??
class Salmon(Fish):
pass
## ??
class Halibut(Fish):
pass
## rover is-a Dog
rover = Dog("Rover")
## ??
satan = Cat("Satan")
## ??
mary = Person("Mary")
## ??
mary.pet = satan
## ??
frank = Employee("Frank", 120000)
## ??
frank.pet = rover
## ??
flipper = Fish()
## ??
crouse = Salmon()
## ??
harry = Halibut()
~~~
## 关于 class Name(object)
记得我曾经强迫让你使用`class Name(object)` 却没告诉你为什么吧,现在你已经知道了“类”和“对象”的区别,我就可以告诉你原因了。如果我早告诉你的话,你可能会晕掉,也学不会这门技术了。
真正的原因是在 Python 早期,它对于`class` 的定义在很多方面都是严重有问题的。当他们承认这一点的时候已经太迟了,所以逼不得已,他们需要支持这种有问题的 `class`。为了解决已有的问题,他们需要引入一种“新类”,这样的话“旧类”还能继续使用,而你也有一个新的正确的类可以使用了。
这就用到了“类即是对象”的概念。他们决定用小写的“object”这个词作为一个类,让你在创建新类时从它继承下来。有点晕了吧?一个类从另一个类继承,而后者虽然是个类,但名字却叫“object”……不过在定义类的时候,别忘记要从 object 继承就好了。
的确如此。一个词的不同就让这个概念变得更难理解,让我不得不现在才讲给你。现在你可以试着去理解“一个是对象的类”这个概念了,如果你感兴趣的话。
不过我还是建议你别去理解了,干脆完全忘记旧格式和新格式类的区别吧,就假设 Python 的 class 永远都要求你加上 (object) 好了,你的脑力要留着思考更重要的问题。
## 附加题
> 1. 研究一下为什么Python添加了这个奇怪的叫做`object`的类,它究竟有什么含义呢?
> 1. 有没有办法把`Class`当作`Object`使用呢?
> 1. 在习题中为 animals、fish、还有 people 添加一些函数,让它们做一些事情。看看当函数在 Animal 这样的“基类(base class)”里和在 Dog 里有什么区别。
> 1. 找些别人的代码,理清里边的“是啥”和“有啥”的关系。
> 1. 使用列表和字典创建一些新的一对应多的“has-many”的关系。
> 1. 你认为会有一种“has-many”的关系吗?阅读一下关于“多重继承(multiple inheritance)”的资料,然后尽量避免这种用法。
## 常见问题
### Q: 这些注释符`## ??`是干什么的?
> 这些是你需要完成的“填空”,你需要填上 "is-a"或者 "has-a"。再读一遍本节练习,看看其他的注释,弄明白我再说什么。
### Q: `self.pet = None`是什么意思?
> 确保给`self.pet`设置了一个默认值`None`
### Q: `super(Employee, self).__init__(name)`实现了什么?
> 这是用来执行父类的`__init__`方法的,上网搜索一下“python super”相关文档,阅读文档的各种建议。
- 序言
- 前言
- 简介
- 0:安装和准备
- 1:第一个程序
- 2:注释和“#”井号
- 3:数字和数学计算
- 4:变量和命名
- 5:更多的变量和打印
- 6:字符串和文本
- 7:更多的打印(输出)
- 8:打印, 打印
- 9:打印, 打印, 打印
- 10:那是什么?
- 11:提问
- 12:提示别人
- 13:参数, 解包, 变量
- 14:提示和传递
- 15:读文件
- 16:读写文件
- 17:更多文件操作
- 18:命名, 变量, 代码, 函数
- 19:函数和变量
- 20:函数和文件
- 21:函数的返回值
- 22:到目前为止你学到了什么?
- 23:阅读代码
- 24:更多的练习
- 25:更多更多的练习
- 26:恭喜你,可以进行一次考试了
- 27:记住逻辑
- 28:布尔表达式
- 29:IF 语句
- 30:Else 和 If
- 31:做出决定
- 32:循环和列表
- 33:while循环
- 34:访问列表元素
- 35:分支和函数
- 36:设计和调试
- 37:复习符号
- 38:列表操作
- 39:字典,可爱的字典
- 40:模块, 类和对象
- 41:学会说面向对象
- 42:对象、类、以及从属关系
- 43:基本的面向对象的分析和设计
- 44:继承Vs.包含
- 45:你来制作一个游戏
- 46:项目骨架
- 47:自动化测试
- 48:更复杂的用户输入
- 49:写代码语句
- 50:你的第一个网站
- 51:从浏览器获取输入
- 52:开始你的web游戏
- 来自老程序员的建议
- 下一步
- 附录A:命令行教程
- 简介
- 安装和准备
- 路径, 文件夹, 名录 (pwd)
- 如果你迷路了
- 创建一个路径 (mkdir)
- 改变当前路径 (cd)
- 列出当前路径 (ls)
- 删除路径 (rmdir)
- 目录切换(pushd, popd)
- 生成一个空文件(Touch, New-Item)
- 复制文件 (cp)
- 移动文件 (mv)
- 查看文件 (less, MORE)
- 输出文件 (cat)
- 删除文件 (rm)
- 退出命令行 (exit)
- 下一步