看完这篇文章,你的python基础知识面向对象就差不多了

第二部分 面向对象

1. 面向对象(OOP)基本概念

面向对象编程 —— 简写

面向过程 —— 怎么做?

把完成某一个需求的 所有步骤 从头到尾 逐步实现

根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数

最后完成的代码,就是顺序地调用 不同的函数

特点:

注重 步骤与过程,不注重职责分工

如果需求复杂,代码会变得很复杂

开发复杂项目,没有固定的套路,开发难度很大!

面向对象 —— 谁来做?

相比较函数,面向对象 是更大的封装,根据职责在 一个对象中封装多个方法

在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)

根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)

最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法

特点:

注重 对象和职责,不同的对象承担不同的职责

更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路

需要在面向过程基础上,再学习一些面向对象的语法

类名这类事物的名字,满足大驼峰命名法

属性这类事物具有什么样的特征

方法这类事物具有什么样的行为

类和对象

是对一群具有相同特征 或者 行为 的事物的一个统称,是抽象的,特征 被称为属性,行为 被称为方法

对象是 由类创建出来的一个具体存在,是类的实例化。

在程序开发中,要设计一个类,通常需要满足一下三个要素:

2. 面向对象基础语法

2.1 dir 内置函数和内置方法

在 Python 中对象几乎是无所不在的,我们之前学习的变量、数据、函数都是对象。

在 Python 中可以使用以下两个方法验证:

在 后输入一个点 ,然后按下 键,iPython 会提示该对象能够调用的方法列表。

使用内置函数 传入 ,可以查看对象内的所有属性及方法

提示格式的方法是 Python 提供的 。

提示利用好 函数,在学习时很多内容就不需要死记硬背了。

2.2 定义简单的类(只包含方法)

面向对象是更大的封装,在 一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!

定义一个只包含方法的类:

方法的定义格式和之前学习过的函数几乎一样,区别在于第一个参数必须是 。

注意类名的 命名规则 要符合大驼峰命名法

当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:

在面向对象开发中,引用的概念是同样适用的!

使用 输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)。

提示:在计算机中,通常使用 十六进制 表示 内存地址。

如果在开发中,希望使用 输出 对象变量 时,能够打印自定义的内容,就可以利用 这个内置方法了:

注意:方法必须返回一个字符串。

2.3 方法中的 self 参数

在 Python 中,要 给对象设置属性,非常的容易,只需要在类的外部的代码中直接通过 设置一个属性即可,但是不推荐使用:

因为:对象属性的封装应该封装在类的内部

由哪一个对象调用的方法,方法内的 就是哪一个对象的引用

在类封装的方法内部, 就表示当前调用方法的对象自己,在方法内部:

可以通过 访问对象的属性,也可以通过 调用对象的其他方法。

调用方法时,程序员不需要传递 self 参数。

在 类的外部,通过访问对象的 属性和方法

在 类封装的方法中,通过 访问对象的 属性和方法

2.4 初始化方法:

当使用 创建对象时,会 自动 执行以下操作:

为对象在内存中分配空间—— 创建对象

为对象的属性设置初始值—— 初始化方法()

方法是专门用来定义一个类具有哪些属性的方法!

在 方法内部使用 就可以定义属性,定义属性之后,再使用 类创建的对象,都会拥有该属性。

在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 方法进行改造

把希望设置的属性值,定义成 方法的参数

在方法内部使用 接收外部传递的参数

在创建对象时,使用 调用

2.5 私有属性和私有方法

应用场景

在实际开发中,对象某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到

私有属性就是对象不希望公开的属性

私有方法就是对象不希望公开的方法

定义方式

定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法:

私有属性和私有方法

伪私有属性和私有方法

Python 中,并没有 真正意义 的 私有

在给 属性、方法 命名时,实际是对名称做了一些特殊处理,使得外界无法访问到

处理方式:在 名称 前面加上 =>

提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法。

3. 封装、继承和多态

面向对象三大特性:

封装根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中

继承实现代码的重用,相同的代码不需要重复的编写

多态不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

3.1 继承

3.1.1 单继承

继承的概念:子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法。

当 父类 的方法实现不能满足子类需求时,可以对方法进行重写(override)

重写 父类方法有两种情况:

覆盖父类的方法:父类的方法实现 和 子类的方法实现完全不同

具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现。

对父类方法进行扩展:子类的方法实现 中 包含 父类的方法实现

在子类中 重写 父类的方法;在需要的位置使用 来调用父类方法的执行代码;其他的位置针对子类的需求,编写 子类特有的代码实现。

关于 super

在 Python 中 是一个 特殊的类

就是使用 创建出来的对象

最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现

调用父类方法的另外一种方式:在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:。目前在 Python 3.x 还支持这种方式,但不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改。

父类的 私有属性 和 私有方法

子类对象不能在自己的方法内部,直接访问 父类的 私有属性 或 私有方法

子类对象 可以通过 父类 的公有方法间接访问到 私有属性 或 私有方法

私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问

私有属性、方法 通常用于做一些内部的事情

3.1.2 多继承

子类 可以拥有多个父类,并且具有 所有父类 的 属性 和 方法,例如:孩子 会继承自己 父亲 和 母亲 的 特性。

Python 中的 MRO算法(Method Resolution Order)

如果 不同的父类 中存在 同名的方法,子类对象 在调用方法时,会调用 哪一个父类中的方法呢?

提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或者方法,应该尽量避免使用多继承

Python 中针对 类 提供了一个 内置属性 可以查看 方法 搜索顺序

在搜索方法时,是按照mro的输出结果 从左至右 的顺序查找的

如果在当前类中 找到方法,就直接执行,不再搜索

如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索

如果找到最后一个类,还没有找到方法,程序报错

MRO 是 method resolution order —— 方法搜索顺序,主要用于 在多继承时判断 方法、属性 的调用 路径

新式类与旧式(经典)类

新式类:以 为基类的类,推荐使用

经典类:不以 为基类的类,不推荐使用

在 Python 3.x 中定义类时,如果没有指定父类,会默认使用 object作为该类的 基类 —— Python 3.x 中定义的类都是新式类,在 Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为 基类。

为了保证编写的代码能够同时在 Python 2.x 和 Python 3.x 运行!今后在定义类时,如果没有父类,建议统一继承自 object:

是 Python 为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用 函数查看。

3.2 多态

面向对象三大特性:

封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中

定义类的准则

继承 实现代码的重用,相同的代码不需要重复的编写

设计类的技巧

子类针对自己特有的需求,编写特定的代码

多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果

增加代码的灵活度

以 继承 和 重写父类方法 为前提

调用方法的技巧,不会影响到类的内部设计

多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!

案例

在 Dog 类中封装方法 game:普通狗只是简单的玩耍

定义 XiaoTianDog 继承自 Dog,并且重写 game 方法:哮天犬需要在天上玩耍

定义 Person 类,并且封装一个 和狗玩 的方法:在方法内部,直接让 狗对象 调用 game 方法

多态示例

Person 类中只需要让 狗对象 调用 game 方法,而不关心具体是 什么狗。

4. 类属性和类方法

4.1 类的结构

通常会把:

创建出来的 对象 叫做类的实例

创建对象的 动作 叫做实例化

对象的属性 叫做实例属性

对象调用的方法 叫做实例方法

每一个对象 都有自己独立的内存空间,保存各自不同的属性

多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

各个不同的属性,独一份的方法

在 Python 中,类是一个特殊的对象。

Python 中 一切皆对象:

class AAA: 定义的类属于类对象

obj1 = AAA() 属于实例对象

在程序运行时,类同样会被加载到内存

在程序运行时,类对象在内存中只有一份,使用 一个类可以创建出很多个对象实例

除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法——类属性、类方法,通过 的方式可以 访问类的属性 或者 调用类的方法

类的结构

4.2 类属性和实例属性

类属性 就是类对象中定义的属性

通常用来记录与这个类相关的特征

类属性不会用于记录具体对象的特征

示例

定义一个 工具类,每件工具都有自己的 :

需求—— 知道使用这个类,创建了多少个工具对象

属性的获取机制

在 Python 中 属性的获取 存在一个向上查找机制

因此,要访问类属性有两种方式:

(不推荐,因为如果使用 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值

4.3 类方法和静态方法

4.3.1 类方法

类属性 就是针对类对象定义的属性

使用 赋值语句 在 关键字下方可以定义 类属性

类属性 用于记录与这个类相关的特征

类方法就是针对类对象定义的方法

类方法内部可以直接访问类属性或者调用其他的类方法

语法如下

类方法需要用修饰器来标识,告诉解释器这是一个类方法

类方法的第一个参数应该是

哪一个类调用的方法,方法内的 就是哪一个类的引用

这个参数和实例方法的第一个参数是 类似

提示使用其他名称也可以,不过习惯使用

通过类名.调用类方法调用方法时,不需要传递 参数

在方法内部

可以通过访问类的属性

也可以通过调用其他的类方法

示例

定义一个工具类,每件工具都有自己的

需求—— 在封装一个 的类方法,输出使用当前这个类,创建的对象个数

4.3.2 静态方法

在开发时,如果需要在中封装一个方法,这个方法:

不需要访问实例属性或者调用实例方法

不需要访问类属性或者调用类方法

这个时候,可以把这个方法封装成一个静态方法

语法如下

静态方法需要用修饰器来标识,告诉解释器这是一个静态方法

通过类名.调用静态方法

示例

静态方法 显示游戏帮助信息

类方法 显示历史最高分

实例方法 开始当前玩家的游戏

探索

实例方法 —— 方法内部需要访问 实例属性

实例方法 内部可以使用 类名. 访问类属性

类方法 —— 方法内部只需要访问 类属性

静态方法 —— 方法内部,不需要访问 实例属性 和 类属性

5. 单例

5.1 单例设计模式

设计模式

设计模式前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

单例设计模式

目的—— 让创建的对象,在系统中只有唯一的一个实例

每一次执行 返回的对象,内存地址是相同的

单例设计模式的应用场景

音乐播放对象

回收站对象

打印机对象

……

5.2 静态方法:

使用类名()创建对象时, 的解释器首先会 调用 方法为对象分配空间

是一个 由 基类提供的内置的静态方法,主要作用有两个:

返回对象的引用

在内存中为对象分配空间

的解释器获得对象的引用后,将引用作为第一个参数,传递给 方法

重写 方法 的代码非常固定!

重写 方法一定要,否则 Python 的解释器得不到分配了空间的对象引用就不会调用对象的初始化方法

注意: 是一个静态方法,在调用时需要主动传递参数

5.3 Python 中的单例

单例—— 让创建的对象,在系统中只有唯一的一个实例

定义一个类属性,初始值是 ,用于记录单例对象的引用

重写 方法

如果类属性,调用父类方法分配空间,并在类属性中记录结果

返回类属性中记录的对象引用

只执行一次初始化工作

在每次使用 创建对象时, 的解释器都会自动调用两个方法:

分配空间

对象初始化

在对 方法改造之后,每次都会得到第一次被创建对象的引用

但是:初始化方法还会被再次调用

需求

初始化动作只被执行一次

解决办法

定义一个类属性 标记是否执行过初始化动作,初始值为

在 方法中,判断 ,如果为 就执行初始化动作

然后将 设置为

这样,再次自动调用 方法时,初始化动作就不会被再次执行

Tips

1、Python 能够自动的将一对括号内部的代码连接在一起:

2、一个对象的 属性 可以是 另外一个类创建的对象。

3、在方法中定义类的属性时,如果 不知道设置什么初始值,可以设置为 ): 关键字 表示 什么都没有,表示一个空对象,没有方法和属性,是一个特殊的常量。可以将 None 赋值给任何一个变量

在 Python 中针对 比较时,建议使用 判断

4、eval() 函数十分强大 —— 将字符串 当成 有效的表达式 来求值 并 返回计算结果

在开发时千万不要使用 eval 直接转换 input 的结果,举个例子:

看完觉得小编文章不错的记得关注小编公众号哦,关注有惊喜!哈哈

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181215A14Q4000?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券