跳转至

面向对象编程

面向对象编程-object oriented programming, 简称 OOP,一种程序思想,一种编程范式, 它将程序组织为一组对象,每个对象包含数据(属性)和操作这些数据的方法(函数)

基本概念

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

std1 = {'name': 'Tom', 'score': 99}
std2 = {'name': 'Yuki', 'score': 100}
通过函数实现,比如打印学生的成绩

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

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

class Student(object):
  def __init__(self, name, score) -> None:
    self.name = name
    self.score = score

  def print_score(self):
    print(f"{self.name}: {self.score}")

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

# 创建对象
james = Student('Tom', 99)
Yuki = Student('Yuki', 100)

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

面向对象的设计思想是从自然界中来的,因为在自然界中, 类(Class) 和实例(Instance) 的概念是很自然的,现实世界中的每个实体,(如汽车,人,狗)都可以看作一个类,他们有属性(如颜色,年龄,品种) 和行为(如行驶,说话,犬叫)等. 上面我们定义的 Student 就是指学生这个概念(类), 而 james 和 Yuki 则是两个具体的 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('Tom', 3)

# 访问对象的属性和方法
print(my_dog.name)
print(my_dog.age)
my_dog.bark()

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

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

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

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

访问限制

在 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 属性
my_dog.get_color()  # 输出: black
上面的代码中,我们定义了 get_color 和 set_color 方法,用于获取和修改__color 私有属性,这样外部代码就可以通过这样两个方法来访问和修改私有属性

为什么要定义私有属性?不定义不就行了吗?因为在方法中,我们可以对参数做检查,避免传入无效的参数

class Dog():
...
  def set_color(self, color):
    if color not in ['red', 'blue','yellow']:
      raise ValueError('Invalid color')
    self.__color = color
这样我们就可以在 set_color 方法中对传入的参数进行检查,确保传入的参数是有效的,这样就提高了代码的健壮性.

回到页面顶部