# 面向对象编程(OOP)
> 原文: [https://javabeginnerstutorial.com/python-tutorial/object-oriented-programming-oop/](https://javabeginnerstutorial.com/python-tutorial/object-oriented-programming-oop/)
现在,我们进行了面向对象的编程。 曾经有过这样一种炒作:每种语言都是围绕对象设计的,而 Python 开发人员 Guido van Rossum 认为“为什么不呢?” 并添加了类以支持面向对象的开发。 一些 python 传福音者认为这是一个错误的决定,有些认为这是一个好方法……
面向对象是一个大话题。 可以写一本关于它的书,但我会坚持在文章的狭小范围内。 或最多有两篇有关 OO 的常规文章和 Python 中的 OO 文章。
## 一般的面向对象
面向对象技术大约在 60 年代后期,但直到 90 年代初才在开发人员中获得了发展空间。 我们将学习以下四个主要原则:
* 封装
* 数据抽象
* 继承
* 多态
如果我想模拟现实生活,我会说 OO 就像一家餐馆。 您可以有两种类型:一种可以在柜台上找到食物,也可以用食物自助服务。 另一个是您进餐的地方,它是由专业服务准备并带给您的。
带有自助服务的第一个版本是命令式语言使用的东西(例如 C 或简单的 Python 脚本),在这里每个人都可以访问所有内容,并且他们可以使用他们想要的东西。 在这种情况下,有时会将碗碟留在桌子上,因为带回碗碟是客户的工作。
第二个版本是 OO。 在那里您可以封装功能,并且只能访问那些公开可用的部分。 如果您已经使用 Java 或 C++ 开发过,您可能会知道公开,受保护和私有访问的概念。 在这种情况下,通过员工来获取食物并带回餐具。 他们知道从何处,何处放东西可以得到什么,而最终用户并不需要了解一切。
如果我们回头看面向对象,那么我们可以说一类是一个对象的定义,一个对象是指定类的实例。
一个类定义了未来对象具有的属性和函数,以及该语言是否使用访问限制,您可以在类定义中告诉公众可以访问哪些部分,该类的扩展还是仅内部使用。
现在是时候深入研究 OOP 的四大原则了。
### 封装
封装是指将数据和函数打包到单个组件中。 但是,在我们的情况下,进入一类,其他编程语言支持其他替代方法。 该类的函数将根据存储在该类的字段中的数据进行操作。
在某些编程语言中,封装用于隐藏信息,或更准确地说:限制对数据和函数的访问。 这些语言包括 C++ 和 Java,例如,您可以在其中使用`private`,`protected`和`public`来限制对字段和方法的访问。 但是在 Python 中没有这样的限制级别。 您可以访问类的每个字段和函数。
但是,有一个约定没有写下来,但是每个 Python 开发人员都知道并且应该知道:名称以双下划线(`__`)开头的类(字段和函数)的成员应视为私有的,不应调用或访问。
### 数据抽象
数据抽象强制将类型的抽象属性与实现细节之间的清晰区分。 抽象属性是那些使用此数据类型对客户端可见的属性(在我们的情况下为类,在其他编程语言中为接口定义),并且实现对客户端隐藏并为私有。
而且由于实现是私有的,因此可以随时间更改(例如,使代码更快),并且客户端不会注意到此更改,因为抽象保持不变。
好吧,在 Python 中,没有什么比其他 OO 语言的接口更好。 您只有一个类,这个类有它的字段和功能。 当然,您可以具有一个“公开”函数作为外部代码的接口,以及一个或多个实现该“公开”函数的逻辑的“私有”函数。 但这不是真正的抽象。
但是,Python 知道用于只读字段的解决方案,这些字段是通过所谓的“获取器”方法即时计算的。 当然,对于读写字段,Python 也使我们也可以使用“设置器”方法。 我们将在后面看到这两个示例。
### 继承
继承是 OOP 的一项关键功能,其中一个类基于另一类的模板/实现(从该类继承)。 这是一种代码重用的基本方法,您可以将子类之间的通用函数和信息封装到一个基类中。
继承模型有不同类型,但是最常见的两种是*单继承*和*多继承*。 Python 使用**多重继承**,这意味着一个类可以根据需要扩展任意多个类。
继承通常与*对象组成*混淆。 有时,新的开发人员会尝试解决继承的所有问题,即使继承应该是对象组合。 对象组合意味着您拥有另一个类的实例的属性,但是您的类没有扩展它。 如果您不知道需要哪一个,请记住以下简单的解决方案:
继承是 **is-a** 关系,意思是*汽车是车辆*。 对象组成是**与**的关系,意味着*汽车具有车轮*(或至少汽车具有车轮)。
### 多态
在 OOP 中,多态是为多个类型提供单个接口。 在 Python 中,这意味着您希望将超类作为参数(例如,执行`isinstance()`检查)并在对象上调用该超类的通用方法。 现在,通过多态,将在所使用的子类中执行该方法的实际实现。
```py
>>> class Animal:
... def sound(self):
... raise NotImplementedError
...
>>> class Dog:
... def sound(self):
... print('woof')
...
>>> class Dog(Animal):
... def sound(self):
... print('woof')
...
>>> class Cat(Animal):
... def sound(self):
... print('meow')
...
>>> def animal_sound(animal):
... if isinstance(animal, Animal):
... animal.sound()
... else:
... print("Not an animal, do not know how to make it sound")
...
>>> cat = Cat()
>>> dog = Dog()
>>> animal_sound(dog)
woof
>>> animal_sound(cat)
meow
```
如您在上面的示例中所看到的,`animal_sound`函数验证该参数是`Animal`,然后调用该特定动物的`sound`方法。
## 什么时候使用 OO?
自然,OO 不是万能的油。 因此,在开发时应考虑使用 OOP。 在本节中,我将更深入地探讨何时应用本章的原理和技术。
确定何时使用面向对象的编程并不容易。 我们必须记住,对象具有数据**和**行为,这使事情变得复杂。 这就是为什么许多 Python 开发人员使用简单的数据结构(列表,集合,字典)和简单的函数的原因,除非确实需要额外的层抽象(我也是)。
现在,如果我们看到使用相同数据集调用函数,则可以考虑将数据封装到一个类中,然后将这些函数添加为类函数以表示行为。
一个简单的示例就是几何图形之外的东西。 在那里,您使用一个 2 元组(一对)来存储点。 点列表代表一个形状(多边形)。 因此,您首先要定义一个列表,其中包含一些表示点的对:
```py
triangle = [(2,3), (5,7), (0,0)]
```
现在,如果您要计算该三角形的周长,可以编写一个如下所示的函数:
```py
import math
def perimeter(points):
perimeter = 0
points_extended = points + [points[0]]
for i in range(len(points)):
perimeter += math.sqrt((points_extended[i][0] - points_extended[i+1][0])**2 + (points_extended[i][1] - points_extended[i+1][1])**2)
return perimeter
```
到达这一点之后,您可能会感觉到有一个对象封装了三角形的所有点(数据)和周长函数(行为)。 如果您想得更多,可以将三角形点的 x 和 y 坐标封装到另一个对象中,然后将两个点的距离计算添加到该对象中。
Python 中有一些类可用于此封装。
在下一篇文章中,我将深入探讨 Python 定义和使用对象(类)的方式,并且我必须事先告诉您还有许多您无法想象的方式。
- JavaBeginnersTutorial 中文系列教程
- Java 教程
- Java 教程 – 入门
- Java 的历史
- Java 基础知识:Java 入门
- jdk vs jre vs jvm
- public static void main(string args[])说明
- 面向初学者的 Java 类和对象教程
- Java 构造器
- 使用 Eclipse 编写 Hello World 程序
- 执行顺序
- Java 中的访问修饰符
- Java 中的非访问修饰符
- Java 中的数据类型
- Java 中的算术运算符
- Java 语句初学者教程
- 用 Java 创建对象的不同方法
- 内部类
- 字符串构建器
- Java 字符串教程
- Java 教程 – 变量
- Java 中的变量
- Java 中的局部变量
- Java 中的实例变量
- Java 引用变量
- 变量遮盖
- Java 教程 – 循环
- Java for循环
- Java 教程 – 异常
- Java 异常教程
- 异常处理 – try-with-resources语句
- Java 异常处理 – try catch块
- Java 教程 – OOPS 概念
- Java 重载
- Java 方法覆盖
- Java 接口
- 继承
- Java 教程 – 关键字
- Java 中的this关键字
- Java static关键字
- Java 教程 – 集合
- Java 数组教程
- Java 集合
- Java 集合迭代器
- Java Hashmap教程
- 链表
- Java 初学者List集合教程
- Java 初学者的Map集合教程
- Java 初学者的Set教程
- Java 初学者的SortedSet集合教程
- Java 初学者SortedMap集合教程
- Java 教程 – 序列化
- Java 序列化概念和示例
- Java 序列化概念和示例第二部分
- Java 瞬态与静态变量
- serialVersionUID的用途是什么
- Java 教程 – 枚举
- Java 枚举(enum)
- Java 枚举示例
- 核心 Java 教程 – 线程
- Java 线程教程
- Java 8 功能
- Java Lambda:初学者指南
- Lambda 表达式简介
- Java 8 Lambda 列表foreach
- Java 8 Lambda 映射foreach
- Java 9
- Java 9 功能
- Java 10
- Java 10 独特功能
- 核心 Java 教程 – 高级主题
- Java 虚拟机基础
- Java 类加载器
- Java 开发人员必须知道..
- Selenium 教程
- 1 什么是 Selenium?
- 2 为什么要进行自动化测试?
- 3 Selenium 的历史
- 4 Selenium 工具套件
- 5 Selenium 工具支持的浏览器和平台
- 6 Selenium 工具:争霸
- 7A Selenium IDE – 简介,优点和局限性
- 7B Selenium IDE – Selenium IDE 和 Firebug 安装
- 7C Selenium IDE – 突破表面:初探
- 7D Selenium IDE – 了解您的 IDE 功能
- 7E Selenium IDE – 了解您的 IDE 功能(续)。
- 7F Selenium IDE – 命令,目标和值
- 7G Selenium IDE – 记录和运行测试用例
- 7H Selenium IDE – Selenium 命令一览
- 7I Selenium IDE – 设置超时,断点,起点
- 7J Selenium IDE – 调试
- 7K Selenium IDE – 定位元素(按 ID,名称,链接文本)
- 7L Selenium IDE – 定位元素(续)
- 7M Selenium IDE – 断言和验证
- 7N Selenium IDE – 利用 Firebug 的优势
- 7O Selenium IDE – 以所需的语言导出测试用例
- 7P Selenium IDE – 其他功能
- 7Q Selenium IDE – 快速浏览插件
- 7Q Selenium IDE – 暂停和反射
- 8 给新手的惊喜
- 9A WebDriver – 架构及其工作方式
- 9B WebDriver – 在 Eclipse 中设置
- 9C WebDriver – 启动 Firefox 的第一个测试脚本
- 9D WebDriver – 执行测试
- 9E WebDriver – 用于启动其他浏览器的代码示例
- 9F WebDriver – JUnit 环境设置
- 9G WebDriver – 在 JUnit4 中运行 WebDriver 测试
- 9H WebDriver – 隐式等待
- 9I WebDriver – 显式等待
- 9J WebDriver – 定位元素:第 1 部分(按 ID,名称,标签名称)
- 9K WebDriver – 定位元素:第 2 部分(按className,linkText,partialLinkText)
- 9L WebDriver – 定位元素:第 3a 部分(按cssSelector定位)
- 9M WebDriver – 定位元素:第 3b 部分(cssSelector续)
- 9N WebDriver – 定位元素:第 4a 部分(通过 xpath)
- 9O WebDriver – 定位元素:第 4b 部分(XPath 续)
- 9P WebDriver – 节省时间的捷径:定位器验证
- 9Q WebDriver – 处理验证码
- 9R WebDriver – 断言和验证
- 9S WebDriver – 处理文本框和图像
- 9T WebDriver – 处理单选按钮和复选框
- 9U WebDriver – 通过两种方式选择项目(下拉菜单和多项选择)
- 9V WebDriver – 以两种方式处理表
- 9W WebDriver – 遍历表元素
- 9X WebDriver – 处理警报/弹出框
- 9Y WebDriver – 处理多个窗口
- 9Z WebDriver – 最大化窗口
- 9AA WebDriver – 执行 JavaScript 代码
- 9AB WebDriver – 使用动作类
- 9AC WebDriver – 无法轻松定位元素? 继续阅读...
- 10A 高级 WebDriver – 使用 Apache ANT
- 10B 高级 WebDriver – 生成 JUnit 报告
- 10C 高级 WebDriver – JUnit 报表自定义
- 10D 高级 WebDriver – JUnit 报告自定义续
- 10E 高级 WebDriver – 生成 PDF 报告
- 10F 高级 WebDriver – 截屏
- 10G 高级 WebDriver – 将屏幕截图保存到 Word 文档
- 10H 高级 WebDriver – 发送带有附件的电子邮件
- 10I 高级 WebDriver – 使用属性文件
- 10J 高级 WebDriver – 使用 POI 从 excel 读取数据
- 10K 高级 WebDriver – 使用 Log4j 第 1 部分
- 10L 高级 WebDriver – 使用 Log4j 第 2 部分
- 10M 高级 WebDriver – 以无头模式运行测试
- Vue 教程
- 1 使用 Vue.js 的 Hello World
- 2 模板语法和反应式的初探
- 3 Vue 指令简介
- 4 Vue Devtools 设置
- 5 数据绑定第 1 部分(文本,原始 HTML,JavaScript 表达式)
- 6 数据绑定第 2 部分(属性)
- 7 条件渲染第 1 部分(v-if,v-else,v-else-if)
- 8 条件渲染第 2 部分(v-if和v-show)
- 9 渲染列表第 1 部分(遍历数组)
- 10 渲染列表第 2 部分(遍历对象)
- 11 监听 DOM 事件和事件修饰符
- 12 监听键盘和鼠标事件
- 13 让我们使用简写
- 14 使用v-model进行双向数据绑定
- 15 表单输入绑定
- 18 类绑定
- Python 教程
- Python 3 简介
- Python 基础知识 - 又称 Hello World 以及如何实现
- 如何在 Windows 中安装 python
- 适用于 Windows,Mac,Linux 的 Python 设置
- Python 数字和字符串
- Python 列表
- Python 集
- Python 字典
- Python 条件语句
- Python 循环
- Python 函数
- 面向对象编程(OOP)
- Python 中的面向对象编程
- Python 3 中的异常处理
- Python 3:猜数字
- Python 3:猜数字 – 回顾
- Python 生成器
- Hibernate 教程
- Hibernate 框架基础
- Hibernate 4 入门教程
- Hibernate 4 注解配置
- Hibernate 4 的实体关系
- Hibernate 4 中的实体继承模型
- Hibernate 4 查询语言
- Hibernate 4 数据库配置
- Hibernate 4 批处理
- Hibernate 4 缓存
- Hibernate 4 审计
- Hibernate 4 的并发控制
- Hibernate 4 的多租户
- Hibernate 4 连接池
- Hibernate 自举