如果说人生的某一刻会遇到瓶颈,那么在 小Python 的世界里,你也将遇到一个瓶颈期,那就是从 面向函数编程 转变为 面向对象编程。相信很多刚开始接触编程的小伙伴,对于什么是面向对象,什么是面向过程都是一脸懵逼的。
1. 对象为何物
1.1 从对象说开去
如果你还是单身狗大军中的一员,又到了谈婚论嫁之时,我相信你考虑的最多的问题,应该是 找对象 这件头等大事。如果你不是众多程序员中的一员,初看 对象 ,你可能会觉得这是再搞相亲大会呢。这么解读,也是未尝不可,只是我们这次是在给 小Python 找对象呢。
现实生活,人可以找对象,动物也可以找对象,植物也会有雄雌,再扩展出去,万物其实都能找对象,万物也都是对象了。
在 小Python 的世界里,它模拟了现实,也就需要有一种类似对象的东西来对应,干脆也叫对象吧。那如何模拟数不尽的对象呢,还是先分个类,然后从种类中具体出一个个对象,比较靠谱。即使是种类,现实世界的种类也是举不胜举,怎么办呢?这个时候我又想起老子的《道德经》有云“一生二,二生三,三生万物”,抓住本质的几个种类,再任由人类去扩展吧,所以 小Python 里也只是提供了部分的种类(类型),但你也能自由扩展(自定义类型),至于怎么个定义法,后文再续。
各位博客可以思考思考,是不是这么个道道呢。如果你作为编程语言的设计者,会怎么考虑呢?
这下好办了吧,我们截至目前也学了一些种类(数据类型)了,如:数值类型、布尔类型、序列类型、映射类型、集合类型,那么像 1024, 512, 3.14, True, ["apple", "banana"], {"name":"《小Python 之路》", "author":"Mr.潘"}
等等,这些就都是对象喽。
1.2 一切皆为对象
小Python 的官方定义为:
对象 是 Python 中对数据的抽象。Python 程序中的所有数据都是由对象或对象间关系来表示的。(从某种意义上说,按照冯·诺依曼的 “存储程序计算机” 模型,代码本身也是由对象来表示的。)
在 小Python 中,所有对象都有如下特点:
一个唯一的ID (ID是一个整型数字,通过id(x)获得)
一个类型 (通过type(x)获得)
数据内容
对象的ID不能修改。
对象的类型不能修改。
有些对象允许修改其内容(ID和类型永远不能被修改),这类对象称为可变对象(Mutable)。有些对象不允许修改其内容,这类对象称为不可变对象(Immutable)。
对象类型被一个类型对象(type object)所表示,该类型对象掌握更多该类型对象的信息,如对象占用了多少内存,对象拥有哪些方法。
1.3 type、object 和 class 的关系
int_a = 2019
string_b = "一切皆对象"
list_c = [1, 3, 5, 7, 9]
def function_d():
pass
print(type(int_a)) # <class 'int'>, int_a 是由int这个类创建的实例
print(type(int)) # <class 'type'>,int 这个类是由 type 这个类创建的实例
print(type(string_b)) # <class 'str'>,string_b 这个类是由 type 这个类创建的实例
print(type(str)) # <class 'str'>,str 这个类是由 type 这个类创建的实例
print(type(list_c)) # <class 'list'>,同上
print(type(list)) # <class 'list'>,同上
print(type(function_d)) # <class 'function'>,同上
print(type(type)) # <class 'type'>,type 是由 type 类自身创建的实例
print(str.__bases__) # (<class 'object'>,),类 str 的基类是 object 这个类
print(type.__bases__) # (<class 'object'>,),类 type 的基类也是 object 这个基类
print(object.__bases__) # (),类object没有基类
对于上面的代码,我们可以得出以下结论:
- 类
type
产生int
实例,类int
又有了 2019 这样的实例 - 类
object
是最顶层的基类 type
也是一个类,同时也是一个对象
想必有些道友,看到上面的代码,已经有点懵逼了。请看下图:
对于上面图片的解读如下:
- object 是一切对象:list、str、dict、tuple的基类,同时 object 是 type 的实例
- 类 type 是自身的实例,同时 type 也继承自 object 类
- 由结论1和结论2,得出一切皆对象,同时一切皆继承自 object 类
2. 编程方法论
2.1 望文生义
先抛出两个编程界跳不过去的 坎 —— 面向过程 与 面向对象。
面向过程(Procedure Oriented 简称PO):
面向过程 = 面向(Verb) + 过程(Noun) = 找(Verb) + 步骤(Noun)
从名字可以看出它是注重过程的。当解决一个问题的时候,面向过程会把事情拆分成: 一个个函数和数据。然后按照一定的顺序,执行完这些函数,等函数执行完了,事情就搞定了,杰出的代表语言:C。
面向对象(Object Oriented简称OO):
面向对象 = 面向(Verb) + 对象(Noun) = 找(Verb) + 对象(Noun)
看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决,杰出的代表语言:C++,Java,Python。
2.2 源于生活
问题: 洗衣机里面放有脏衣服,怎么洗干净?
面向过程的解决方法:
- 执行加洗衣粉方法;
- 执行加水方法;
- 执行洗衣服方法;
- 执行清洗方法;
- 执行烘干方法;
以上就是将解决这个问题的过程拆成一个个方法(是没有对象去调用的),通过一个个方法的执行来解决问题。
面向对象的解决方法:
- 我先弄出两个对象:“洗衣机”对象和“人”对象
- 针对对象“洗衣机”加入一些属性和方法:“洗衣服方法”“清洗方法”、“烘干方法”
- 针对对象“人”加入属性和方法:“加洗衣粉方法”、“加水方法”
- 然后执行
- 人.加洗衣粉
- 人.加水
- 洗衣机.洗衣服
- 洗衣机.清洗
- 洗衣机.烘干
解决同一个问题 ,面向对象编程就是先抽象出对象,然后用对象执行方法的方式解决问题。
2.3 孰优孰劣
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展。
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护。
缺点:性能比面向过程低。
3. 归于编程
3.1 类 和 对象
通常为了便于我们去识别、记忆多个事物,我们首选的方案是应该根据相同特征、行为归归类,然后再对号入座,这样可能更加高效。正如“人是人他妈生的,妖是妖他妈生的”。下面先看个示例:
请将以下信息归类:李白、徐悲鸿、张大千、杜甫、陶渊明、齐白石。
诗人:李白、杜甫、陶渊明
画家:徐悲鸿、张大千、齐白石
我们归类的标准应该是:“李白、杜甫、陶渊明”都是会写诗的历史名人,“徐悲鸿、张大千、齐白石”都是会画画的历史名人。
所以我们可以用 小Python 的话术说“诗人”和“画家”就是“类”,“李白、杜甫、陶渊明”和“徐悲鸿、张大千、齐白石”就是“对象”(也叫 类的实例化 )。
专业解释:
对象:一个具体化事物,更多的称之为一个实例,如:李白、齐白石
类:一批具有相同属性和行为的对象的抽象表示,如:诗人、画家
3.2 小Python 造物
"""
文件名:Poet.py
用途:根据“李白、杜甫、陶渊明”几个人的共有特性,抽象出一个名词“诗人”
作者:Mr.潘
"""
# class:定义 类 型关键字
# Poet:类 的名称,遵从标识符的命名规范,推荐使用 大驼峰命名法
class Poet:
def __init__(self, name, dynasty, genre):
"""初始化实例对象的方法.
self: 指向每一个实例的固有参数,通过其访问实例属性和对象方法
name: 姓名
dynasty: 朝代
genre: 诗派
"""
self.name = name # 实例属性,将实参 name 赋值给实例的一个属性
self.dynasty = dynasty
self.genre = genre
# 类的实例化过程 --- 小Python 造人
libai = Poet("李白", "唐朝", "浪漫主义") # 基于 诗人 创建了一个具体的对象 李白,自动执行 Poet 的 __init__() 方法
print(libai.name) # 李白的姓名
print(libai.dynasty) # 李白所属朝代
print(libai.genre) # 李白的诗派
dufu = Poet("杜甫", "唐朝", "现实主义")
print(dufu.name) # 杜甫的姓名
print(dufu.dynasty) # 杜甫所属朝代
print(dufu.genre) # 杜甫的诗派
3.3 为你写诗
"""
文件名:write_poems_for_you.py
用途:给诗人添加写诗能力,并拓展类的其它特性
作者:Mr.潘
"""
class Poet:
count = 0 # 类属性,记录基于该类创建的实例数量,类的一个属性,记录类的一个特性
def __init__(self, name, dynasty, genre):
"""初始化实例对象的方法.
self: 指向每一个实例的固有参数,通过其访问实例属性和对象方法
name: 姓名
dynasty: 朝代
genre: 诗派
"""
self.name = name
self.dynasty = dynasty
self.genre = genre
Poet.count += 1 # 每执行一次该方法,就将 count + 1
def description(self):
"""实例的一个自我介绍的方法.
"""
print("大家好,我是{},是{}的一位{}诗人。".format(self.name, self.dynasty, self.genre))
def write_poet(self, title, content):
"""实例的一个写诗的方法.
title: 诗的主题
content: 诗的内容
"""
print("\t{}\t".format(title))
for line in content.split("。"):
print(line)
@classmethod
def show_count(cls):
"""类方法,针对于类定义的方法,需要 @classmethod 标识.
cls: 由 哪一个类 调用的方法,方法内的 cls 就是哪一个类的引用
"""
print("当前诗人的数量为{}".format(cls.count))
@staticmethod
def show_title():
"""静态方法,仅仅是为了封装在类中,不需要访问类信息,也不需要访问实例信息."""
print("********************** 为你写诗 **********************")
# 类的实例化过程 --- 小Python 造人
Poet.show_title() # 通过类名访问静态方法
libai = Poet("李白", "唐朝", "浪漫主义")
libai.description() # 李白做自我介绍
libai.write_poet("静夜思", "床前明月光,疑是地上霜。举头望明月,低头思故乡。") # 李白赋诗一首
dufu = Poet("杜甫", "唐朝", "现实主义")
dufu.description() # 杜甫做自我介绍
dufu.write_poet("绝句", "两个黄鹂鸣翠柳,一行白鹭上青天。窗含西岭千秋雪,门泊东吴万里船。") # 杜甫赋诗一首
Poet.show_count() # 通过类名访问类方法