173.md 7.8 KB
Newer Older
W
init  
wizardforcel 已提交
1 2 3 4
# 面向对象编程(OOP)

> 原文: [https://javabeginnerstutorial.com/python-tutorial/object-oriented-programming-oop/](https://javabeginnerstutorial.com/python-tutorial/object-oriented-programming-oop/)

W
wizardforcel 已提交
5
现在,我们进行了面向对象的编程。 曾经有过这样一种炒作:每种语言都是围绕对象设计的,而 Python 开发人员 Guido van Rossum 认为“为什么不呢?” 并添加了类以支持面向对象的开发。 一些 python 传福音者认为这是一个错误的决定,有些认为这是一个好方法……
W
init  
wizardforcel 已提交
6

W
wizardforcel 已提交
7
面向对象是一个大话题。 可以写一本关于它的书,但我会坚持在文章的狭小范围内。 或最多有两篇有关 OO 的常规文章和 Python 中的 OO 文章。
W
init  
wizardforcel 已提交
8 9 10

## 一般的面向对象

W
wizardforcel 已提交
11
面向对象技术大约在 60 年代后期,但直到 90 年代初才在开发人员中获得了发展空间。 我们将学习以下四个主要原则:
W
init  
wizardforcel 已提交
12 13 14

*   封装形式
*   数据抽象
W
wizardforcel 已提交
15
*   继承
W
wizardforcel 已提交
16
*   多态
W
init  
wizardforcel 已提交
17

W
wizardforcel 已提交
18
如果我想模拟现实生活,我会说 OO 就像一家餐馆。 您可以有两种类型:一种可以在柜台上找到食物,也可以用食物自助服务。 另一个是您进餐的地方,它是由专业服务准备并带给您的。
W
init  
wizardforcel 已提交
19

W
wizardforcel 已提交
20
带有自助服务的第一个版本是命令式语言使用的东西(例如 C 或简单的 Python 脚本),在这里每个人都可以访问所有内容,并且他们可以使用他们想要的东西。 在这种情况下,有时会将碗碟留在桌子上,因为带回碗碟是客户的工作。
W
init  
wizardforcel 已提交
21

W
wizardforcel 已提交
22
第二个版本是 OO。 在那里您可以封装功能,并且只能访问那些公开可用的部分。 如果您已经使用 Java 或 C++ 开发过,您可能会知道公开,受保护和私有访问的概念。 在这种情况下,通过员工来获取食物并带回餐具。 他们知道从何处,何处放东西可以得到什么,而最终用户并不需要了解一切。
W
init  
wizardforcel 已提交
23 24 25 26 27

如果我们回头看面向对象,那么我们可以说一类是一个对象的定义,一个对象是指定类的实例。

一个类定义了未来对象具有的属性和功能,以及该语言是否使用访问限制,您可以在类定义中告诉公众可以访问哪些部分,该类的扩展还是仅内部使用。

W
wizardforcel 已提交
28
现在是时候深入研究 OOP 的四大原则了。
W
init  
wizardforcel 已提交
29 30 31 32 33

### 封装形式

封装是指将数据和功能打包到单个组件中。 但是,在我们的情况下,进入一类,其他编程语言支持其他替代方法。 该类的功能将根据存储在该类的字段中的数据进行操作。

W
wizardforcel 已提交
34
在某些编程语言中,封装用于隐藏信息,或更准确地说:限制对数据和功能的访问。 这些语言包括 C++ 和 Java,例如,您可以在其中使用 private,protected 和 public 来限制对字段和方法的访问。 但是在 Python 中没有这样的限制级别。 您可以访问类的每个字段和功能。
W
init  
wizardforcel 已提交
35

W
wizardforcel 已提交
36
但是,有一个约定没有写下来,但是每个 Python 开发人员都知道并且应该知道:名称以双下划线(__)开头的类(字段和函数)的成员应视为私有的,不应调用或访问。
W
init  
wizardforcel 已提交
37 38 39 40 41 42 43

### 数据抽象

数据抽象强制将类型的抽象属性与实现细节之间的清晰区分。 抽象属性是那些使用此数据类型对客户端可见的属性(在我们的情况下为类,在其他编程语言中为接口定义),并且实现对客户端隐藏并为私有。

而且由于实现是私有的,因此可以随时间更改(例如,使代码更快),并且客户端不会注意到此更改,因为抽象保持不变。

W
wizardforcel 已提交
44
好吧,在 Python 中,没有什么比其他 OO 语言的接口更好。 您只有一个班级,这个班级有它的字段和功能。 当然,您可以具有一个“公开”函数作为外部代码的接口,以及一个或多个实现该“公开”函数的逻辑的“私有”函数。 但这不是真正的抽象。
W
init  
wizardforcel 已提交
45

W
wizardforcel 已提交
46
但是,Python 知道用于只读字段的解决方案,这些字段是通过所谓的“ getter”方法即时计算的。 当然,对于读写字段,Python 也使我们也可以使用“ setter”方法。 我们将在后面看到这两个示例。
W
init  
wizardforcel 已提交
47

W
wizardforcel 已提交
48
### 继承
W
init  
wizardforcel 已提交
49

W
wizardforcel 已提交
50
继承是 OOP 的一项关键功能,其中一个类基于另一类的模板/实现(从该类继承)。 这是一种代码重用的基本方法,您可以将子类之间的通用功能和信息封装到一个基类中。
W
init  
wizardforcel 已提交
51

W
wizardforcel 已提交
52
继承模型有不同类型,但是最常见的两种是*单继承**多继承*。 Python 使用**多重继承**,这意味着一个类可以根据需要扩展任意多个类。
W
init  
wizardforcel 已提交
53 54 55 56 57

继承通常与*对象组成*混淆。 有时,新的开发人员会尝试解决继承的所有问题,即使继承应该是对象组合。 对象组合意味着您拥有另一个类的实例的属性,但是您的类没有扩展它。 如果您不知道需要哪一个,请记住以下简单的解决方案:

继承是 **is-a** 关系,意思是*汽车是车辆*。 对象组成是**与**的关系,意味着*汽车具有车轮*(或至少汽车具有车轮)。

W
wizardforcel 已提交
58
### 多态
W
init  
wizardforcel 已提交
59

W
wizardforcel 已提交
60
在 OOP 中,多态是为多个类型提供单个接口。 在 Python 中,这意味着您希望将超类作为参数(例如,执行 isinstance()-check)并在对象上调用该超类的通用方法。 现在,通过多态,将在所使用的子类中执行该方法的实际实现。
W
init  
wizardforcel 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

```java
>>> 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
```

W
wizardforcel 已提交
93
如您在上面的示例中所看到的,animal_sound 函数验证该参数是 Animal,然后调用该特定动物的 sound 方法。
W
init  
wizardforcel 已提交
94

W
wizardforcel 已提交
95
## 什么时候使用 OO?
W
init  
wizardforcel 已提交
96

W
wizardforcel 已提交
97
自然,OO 不是万能的油。 因此,在开发时应考虑使用 OOP。 在本节中,我将更深入地探讨何时应用本章的原理和技术。
W
init  
wizardforcel 已提交
98

W
wizardforcel 已提交
99
确定何时使用面向对象的编程并不容易。 我们必须记住,对象具有数据**和**行为,这使事情变得复杂。 这就是为什么许多 Python 开发人员使用简单的数据结构(列表,集合,字典)和简单的函数的原因,除非确实需要额外的层抽象(我也是)。
W
init  
wizardforcel 已提交
100 101 102

现在,如果我们看到使用相同数据集调用函数,则可以考虑将数据封装到一个类中,然后将这些函数添加为类函数以表示行为。

W
wizardforcel 已提交
103
一个简单的例子就是几何图形之外的东西。 在那里,您使用一个 2 元组(一对)来存储点。 点列表代表一个形状(多边形)。 因此,您首先要定义一个列表,其中包含一些表示点的对:
W
init  
wizardforcel 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

```java
triangle = [(2,3), (5,7), (0,0)]
```

现在,如果您要计算该三角形的周长,可以编写一个如下所示的函数:

```java
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
```

W
wizardforcel 已提交
122
到达这一点之后,您可能会感觉到有一个对象封装了三角形的所有点(数据)和周长函数(行为)。 如果您想得更多,可以将三角形点的 x 和 y 坐标封装到另一个对象中,然后将两个点的距离计算添加到该对象中。
W
init  
wizardforcel 已提交
123

W
wizardforcel 已提交
124
Python 中有一些类可用于此封装。
W
init  
wizardforcel 已提交
125

W
wizardforcel 已提交
126
在下一篇文章中,我将深入探讨 Python 定义和使用对象(类)的方式,并且我必须事先告诉您还有许多您无法想象的方式。
W
init  
wizardforcel 已提交
127