Python入门教程丨3.1 Python高级用法:面向对象编程,封装,继承和多态

78 阅读8分钟

面向对象编程(OOP)是一种软件开发方法和编程范式,它将现实世界中的事物抽象为程序中的对象,我们身边的一切——手机、杯子、汽车,都可以看作是“对象”。每个对象都有自己的属性(特征)和行为(功能)。


1. 创建一个对象——类和实例

每个对象都有一个“模板”,这个模板就是。比如,我们可以定义一个“人类”的模板,然后通过这个模板创建具体的人。


1.1 创建一个Person

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

我们首先创建了一个Person类,这是一个抽象的人,泛指所有的人,专门用来生成具体的人,类一般首字母大写。

__init__方法是这个类的初始化,每次我们用Person创建一个人,名字和年龄都会被传递给对象,就像给新生儿起名字和记录年龄一样。

self 是对象自己,像英语中的myself,用self特指对象自己。


1.2 实例化对象和调用方法

方法就是这个对象的行为,比如每个人都会说“你好,我叫XXX,今年XX岁。”在代码中,我们也可以给对象这种能力!

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        return f"大家好,我是{self.name},今年{self.age}岁!"

现在,让我们让小明来亮相吧,这个过程叫实例化对象,就是通过抽象的Person类创建一个具体的人:小明。

使用实例名+.+方法名就可以调用对象中的方法,例如自我介绍。

xiaoming = Person("小明", 18)
print(xiaoming.introduce())

运行后,你会看到:

大家好,我是小明,今年18岁!

1.3 添加属性和方法

对象不可能一成不变,我们可以很方便的拓展类的功能。比如,人会长大、会跑步、会学习。这些行为可以用方法来实现,而“长大”的结果可以通过属性来体现,比如有了新的兴趣爱好。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.hobby = hobby
        
    def introduce(self):
        return f"大家好,我是{self.name},今年{self.age}岁!"

    def birthday(self,hobby):
        self.age += 1
        sleg.hobby = hobby
        print(f"今天是我的生日,我今年学会了{slef.hobby},现在我{self.age}岁啦!")

现在,我们让小明过生日了:

xiaoming.birthday("打篮球")

运行后:

今天是我的生日,我今年学会了打篮球,现在我19岁啦!

2. 对象之间的关系

现实中,人不是孤单存在的。他们有家庭、朋友,也有不同的身份。对象也一样,可能会有各种联系,让我们看看如何通过类和对象表示这些复杂关系。

2.1 包含关系

假设一个人有一辆车,我们可以用一个类嵌套另一个类来表示这种关系。

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color

class Person:
    def __init__(self, name, age, car):
        self.name = name
        self.age = age
        self.car = car

    def show_car(self):
        print(f"我的车是{self.car.color}的{self.car.brand}!")

创建对象并测试:

my_car = Car("特斯拉", "红色")
xiaoming = Person("小明", 18, my_car)
xiaoming.show_car()
#运行结果:我的车是红色的特斯拉!

这个过程中我们先实例化了对象Car,再实例化对象人,我们将 car 作为人的一个属性,车包含于人。

然后就可以直接在人这个对象中调用车这个对象,调用过程中就是多加了一个.self.car.brand

2.2 继承关系

有时,一个类可以是另一个类的“特殊版本”。比如,学生是一种特殊的“人”,我们可以用继承来实现。

即学生具有“人类”的所有特点,但是其自身又有一些特别的地方,比如学生有学校。

用概念来说,就是子类父类之间的关系,子类可以继承父类的所有属性和方法。

class Student(Person):
    def __init__(self, name, age, school):
        super().__init__(name, age)
        self.school = school

    def introduce(self):
        return f"大家好,我是{self.name},今年{self.age}岁,我在{self.school}上学!"

super() 是一个内置函数,它的主要作用是调用当前类的父类的 __init__ 方法,并且将 nameage 作为参数传递给父类的构造函数。

现在我们可以创建学生对象了:

xiaohong = Student("小红", 16, "北京高中")
print(xiaohong.introduce())
#运行结果:大家好,我是小红,今年16岁,我在北京高中上学!

2.3 “兄弟”关系

“兄弟”关系是我自己起的一个名字,方便大家理解,所谓“兄弟”关系就是继承了同一个类的几个不同的类之间的关系,他们虽然都继承了父类,但是具体的表现又不同(龙生九子,各有不同),这实际上称为多态

比如我们刚刚写了学生类,我们再写一个教师类,同样也继承人类的所有属性和方法,虽然学生和教师属于不同的类,但是他们同一个方法的内容可以不同。

class Teacher(Person):
    def __init__(self, name, age, subject):
        super().__init__(name, age)
        self.subject = subject

    def introduce(self):
        return f"大家好,我是{self.name},教{self.subject}!"

让学生和老师各自介绍自己:

xiaoming = Student("小明", 18, "北大")
laozhang = Teacher("老张", 40, "数学")
print(xiaoming.introduce())
print(laozhang.introduce())

输出:

大家好,我是小明,今年18岁,我在北大上学!
大家好,我是老张,教数学!

3. 面向对象的三大特点

我们刚刚通过一些例子感受了一下面向对象的特点,接着我们来详细讲解一下具体的概念和作用。

3.1 封装(Encapsulation)

**定义:**封装是指将数据(属性)和行为(方法)封装在类中,通过访问权限控制外界对类中数据的访问和修改。
目标:保护数据,减少耦合,提高代码的可维护性。

作用:

  1. 隐藏内部细节:防止外界直接访问和修改对象的内部数据,确保数据的安全性和完整性。
  2. 提供统一接口:通过方法(getter 和 setter)对外提供访问接口,保证对象的操作行为统一、受控。
  3. 模块化:使代码结构更清晰,易于维护和扩展。

示例:银行账户的封装

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner        # 账户持有人(公开属性)
        self.__balance = balance  # 账户余额(私有属性)

    # 获取余额
    def get_balance(self):
        return self.__balance

    # 存款方法
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"{amount} 元已存入账户,现在余额为 {self.__balance} 元。")
        else:
            print("存款金额无效。")

    # 取款方法
    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"{amount} 元已取出账户,现在余额为 {self.__balance} 元。")
        else:
            print("余额不足或金额无效。")

# 使用示例
account = BankAccount("小明", 1000)
print(account.get_balance())  # 输出:1000
account.deposit(500)          # 输出:500 元已存入账户,现在余额为 1500 元。
account.withdraw(200)         # 输出:200 元已取出账户,现在余额为 1300 元。

封装使得账户余额等敏感信息得到了保护,只有通过提供的接口方法才能进行修改或查看,无法直接通过.+属性名的方式进行修改,提高了安全性,避免了误操作。


3.2 继承(Inheritance)

**定义:**继承是一种机制,通过定义子类(子类继承父类),子类可以直接拥有父类的属性和方法,且可以扩展或重写它们。
目标:实现代码复用,减少重复代码,建立类之间的层次关系。

作用:

  1. 代码复用:子类继承父类,无需重复编写相同代码。
  2. 扩展性:子类可以在父类基础上扩展新功能。
  3. 多层次关系:建立清晰的类层次结构,反映对象的共性和特性。

继承相对好理解,可以参考 2.2 中学生类继承了人类的例子。


3.3 多态(Polymorphism)

**定义:**多态是指子类可以根据自己的需要重写父类的方法,并通过父类引用调用时表现出不同的行为。
目标:在不同类中实现同一接口,增强代码的灵活性和扩展性。

作用:

  1. 提高灵活性:同一操作可以针对不同对象表现出不同行为。
  2. 简化代码:对一组对象进行统一操作,无需关注具体对象的类型。
  3. 增强扩展性:新对象只需遵循接口定义即可融入系统。

我们这里再拿经典的动物类来举例,便于大家体会这个概念。

# 定义父类
class Animal:
    def speak(self):
        pass  # 父类方法是抽象的,留给子类实现

# 定义子类
class Dog(Animal):
    def speak(self):
        return "汪汪!"

class Cat(Animal):
    def speak(self):
        return "喵喵!"

# 使用多态
animals = [Dog(), Cat()]  # 存储不同子类对象的列表

for animal in animals:
    print(animal.speak())  # 输出:汪汪!  喵喵!

动物都会叫,但是不同动物的叫声不同,这就是对多态的直观感受。


4. 小结

面向对象编程正是因为具有如下特点,使得其在开发中应用非常广泛。

  • 封装:隐藏细节,保护数据(例:银行账户)。
  • 继承:代码复用,减少冗余(例:动物行为)。
  • 多态:灵活扩展,统一接口(例:几何图形面积计算)。
  • 低耦合:模块独立,便于扩展(例:数据存储策略)。

通过这些特性,OOP帮助开发者构建可读、可维护、可扩展的代码结构,是解决复杂问题的有效方法。

🙋🏻‍♀️比如我们需要设计一个学生管理系统,使用面向对象的思想就能将问题大幅度简化,如创建一个 Student 基类,包含通用属性(如姓名、学号)和方法(如打印成绩),并可以扩展特有功能,例如论文管理功能。

我们套面向对象的三大特点来体会:

  • 封装:每个学生对象独立管理自己的数据和方法,避免外部直接修改。
  • 继承:不同学生类型复用通用代码,减少重复。
  • 多态:统一管理不同类型的学生,例如本科生和研究生、不同专业的学生。

⭐️面向对象的概念相对抽象,要多结合现实生活中的例子去理解。