logo

该视频仅会员有权观看

立即开通课程「Python 入门」权限。

¥
199
/ 年

面向对象编程

面向对象编程 —— Object Oriented Programming,简称 OOP,是一种程序设计思想,一种编程范式,它将程序组织为一组对象,每个对象包含数据(属性)和操作这些数据的方法(函数)。

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

基本概念

在 python 中,一切皆对象。我们可以用一个例子来说明面向过程和面向对象在程序流程上的不同之处。假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个字典表示:

std1 = {'name': 'James', 'score': 98} std2 = {'name': 'Jordan', 'score': 90}

而处理学生成绩可以通过函数实现,比如打印学生的成绩:

def print_score(std): print(f"{std['name']}: {std['score']}")

如果采用面向对象的程序设计思想,我们首先要思考的不是程序的执行流程,而是学生这种数据类型应该被视为一个对象,这个对象拥有 namescore 这两个属性。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后给对象发一个 print_score 消息,让对象自己把自己的数据打印出来。

class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print(f"{self.name}: {self.score}")

上面代码中我们使用 class 关键字定义了一个 Student 类,这个类包含 namescore 两个属性,以及一个 print_score 方法,用于打印学生的成绩。然后我们就可以创建 Student 类的实例对象,然后调用 print_score 方法就可以打印学生的成绩了。

james = Student('James', 98) jordan = Student('Jordan', 90) james.print_score() jordan.print_score()

面向对象的程序设计思想是把对象作为程序的基本单元,一个对象包含了数据和操作数据的方法。在 Python 中,所有数据类型都可以视为对象,所以我们可以自定义对象,自定义对象的属性和方法,这样就可以实现面向对象的程序设计。

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。现实世界中的每个实体(如汽车、人、狗)都可以看作是一个类,它们有属性(如颜色、年龄、品种)和行为(如行驶、说话、吠叫)等。上面我们定义的 Student 就是指学生这个概念(类),而 James 和 Jordan 则是两个具体的 Student 对象(实例)。

定义

类是对象的模板,定义了对象的属性和方法。对象是类的实例,通过类创建,在 python 中通过 class 关键字定义一个类,然后后面是类的名称,类的名称通常是大写字母开头的单词,然后是 (),括号中是类的基类,如果没有基类可以省略,默认是 object 类(object 类是 python 中所有类的基类)。

# 定义一个类 class Dog: def __init__(self, name, age): self.name = name self.age = age def bark(self): print(f"{self.name} says Woof! I'm {self.age} years old.") # 创建一个对象 my_dog = Dog("lucy", 3) # 访问对象的属性和方法 print(my_dog.name) # 输出: lucy print(my_dog.age) # 输出: 5 my_dog.bark() # 输出: lucy says Woof! I'm 3 years old.

上面代码中我们定义了一个 Dog 类,包含 nameage 属性,以及 bark 方法。通过类创建 my_dog 对象,并访问其属性和方法。

其中有一个比较重要的方法 __init__,该方法是类的构造方法,用于初始化对象的属性。也就是你的类有什么属性,你就在 __init__ 方法中定义什么属性。

另外我们可以看到类里面的所有方法第一个参数都是 self 关键字,self 参数是一个指向对象本身的引用,类,类的方法必须包含 self 参数,用于访问对象的属性和方法,比如我们要在 bark 方法中访问 name 属性,就可以通过 self.name 来访问。

定义了 __init__ 方法后,创建对象时就必须传入与 __init__ 方法匹配的参数,但是不需要传入 self 参数,python 解释器会自动传递的,比如我们这里创建 my_dog 对象时传入了 lucy3 两个参数,这两个参数会传递给 __init__ 方法,然后初始化对象的属性。这样当我们调用 bark 方法时,就可以访问到 nameage 属性了。

访问限制

在 Class 内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线 __,这样这个属性就是私有属性,外部就无法直接访问,如下所示:

# 定义一个类 class Dog: def __init__(self, name, age): self.name = name self.age = age self.__color = 'white' # 私有属性 def get_color(self): return self.__color def set_color(self, color): self.__color = color # 创建一个对象 my_dog = Dog("lucy", 3) print(my_dog.__color) # 报错: AttributeError: 'Dog' object has no attribute '__color' my_dog.__color = 'black' # 不会修改 __color 属性

在上面的代码中,我们在 Dog 类中定义了一个 __color 私有属性,私有属性的名称前加上两个下划线 __,外部就无法直接访问,如果尝试访问会报错。这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

但是如果外部代码要获取这个私有属性怎么办?可以给类增加 getter、setter 方法,用于获取和修改私有属性,如下所示:

# 定义一个类 class Dog: def __init__(self, name, age): self.name = name self.age = age self.__color = 'white' # 私有属性 def get_color(self): return self.__color def set_color(self, color): self.__color = color # 创建一个对象 my_dog = Dog("lucy", 3) my_dog.get_color() # 输出: white my_dog.set_color('black') # 修改 __color 属性

在上面的代码中,我们定义了 get_colorset_color 方法,用于获取和修改 __color 私有属性,这样外部代码就可以通过这两个方法来访问和修改私有属性了。

你可能也发现了我们不定义成私有属性不就可以了吗?为什么要还要大费周折去定义私有属性,然后再定义 getter、setter 方法呢?因为在方法中,我们可以对参数做检查,避免传入无效的参数:

class Student(object): ... def set_color(self, color): if color not in ['red', 'blue', 'yellow']: raise ValueError('Invalid color')

这样我们就可以在 set_color 方法中对传入的参数进行检查,确保传入的参数是有效的,这样就提高了代码的健壮性。

上面我们讲的这些是面向对象编程的一些基本概念,面向对象最主要的特点就是封装、继承和多态,这三个特点是面向对象编程的基石,其实上面我们讲的访问限制就是封装的一种体现,封装是将对象的属性和方法包装在一起,隐藏对象的内部实现细节,通过公共接口访问,这样就提高了代码的可维护性和可重用性。其他的继承和多态我们会在后面的章节中详细讲解。