Python基础语法入门(第十七天)——面向对象三要素

文章介绍了面向对象编程的三个核心概念:封装、继承和多态。封装是将数据和操作封装在对象中,保护数据和实现细节,通过访问修饰符、Getter和Setter方法实现。继承允许子类继承父类的属性和方法,实现代码重用和定制。多态允许使用相同的接口处理不同类型的对象,包括编译时和运行时多态。文章提供了Python代码示例来说明这些概念,并给出一个图书管理系统作为练习题目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇文章中的练习参考答案:

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

    def show(self):
        print("My name is %s" % self.name)
        print("I'm %d years old." % self.age)
        print("%s is my favorite sport." % self.sport)


xiaohong = User('xiaohong', 18, 'badminton')
xiaoming = User('xiaoming', 18, 'Esports')
xiaohong.show()
xiaoming.show()

今日学习目标

掌握面向对象的三要素:
1.封装
2.继承
3.多态

一、封装

定义

封装是面向对象编程中的重要概念之一,它是将数据和操作封装在对象中的机制。

封装的目的是隐藏对象的内部细节和实现细节,只暴露必要的接口供外界访问和使用。通过封装,可以实现以下几个重要的目标:

1.数据隐藏:封装可以隐藏对象的内部数据,使其对外部不可见。只有通过对象的公共接口(方法)才能访问和修改对象的数据,这样可以防止外部直接修改对象内部的数据,提高了数据的安全性和完整性。

2.实现细节隐藏:封装也可以隐藏对象的具体实现细节。对象对外部只公开一些抽象的方法,而不暴露内部的实现细节,这样可以在不影响外部使用的前提下,随时改变和优化对象的实现方式。

3.接口定义:封装通过定义对象的公共接口(方法)来提供对对象的访问和操作。对象的用户不需要知道对象内部的实现细节,只需要使用接口提供的方法来完成所需的操作。

4.代码组织:封装可以将相关的数据和操作组织在一个对象中,使代码更加模块化和可维护。对象拥有自己的数据和方法,这样可以将相关的功能集中在一个对象中,便于开发、测试和理解。

在实践中,封装可以通过以下方式来实现:

  • 定义类:类是封装的基本单位,可以将相关的数据和方法封装在一个类中,并提供类的接口供外界使用。
  • 访问修饰符:通过使用访问修饰符(如公有、私有和受保护),可以控制属性和方法的访问范围,从而实现对对象的封装。
  • Getter和Setter方法:可以通过定义Getter和Setter方法来控制对象属性的访问和修改。Getter方法用于获取属性的值,Setter方法用于设置属性的值,可以在方法中添加逻辑来对属性进行验证和控制。

封装是面向对象编程的核心特性之一,它提供了一种更安全、可靠和模块化的方式来设计和组织代码。封装可以提高代码的可维护性、可重用性和安全性,使得我们能够更加专注于对象的使用和功能的实现。

请看下方代码:

class Person:
    def __init__(self, name, age):
        self._name = name  # 使用单个下划线作为属性的约定,表示属性是受保护的,但仍可访问
        self._age = age

    # Getter方法
    def get_name(self):
        return self._name

    # Setter方法
    def set_name(self, name):
        self._name = name

    def speak(self):
        print(f"I am {self._name}, {self._age} years old.")

# 创建对象
person = Person("John", 25)

# 访问属性
print(person.get_name())   # 输出:John

# 修改属性
person.set_name("Michael")
print(person.get_name())   # 输出:Michael

# 使用方法
person.speak()   # 输出:I am Michael, 25 years old.

在上面的代码中,我们定义了一个名为Person的类,它具有_name_age两个属性。这些属性被命名为以单个下划线开头,这是一种约定,表示这些属性是受保护的,应该在类的外部进行访问。虽然它们仍然可以通过外部访问,但是作为开发者,我们应该将其视为不应直接访问的属性。

为了访问和修改这些属性,我们提供了Getter和Setter方法。这些方法允许外部代码通过调用方法来获取和设置属性的值。在Getter方法中,我们简单地返回相应的属性值。在Setter方法中,我们将传入的新值分配给属性。通过这种方式,我们可以在Getter和Setter方法中添加逻辑来验证和控制属性的访问和修改。

在主程序中,我们创建了一个Person对象,传入名称和年龄参数。通过get_name()方法,我们可以获取名称属性的值,并进行输出。然后,我们通过set_name()方法将名称属性修改为新值。最后,我们使用speak()方法来展示该对象的行为。

这个示例展示了Python中如何使用Getter和Setter方法实现封装,以及通过属性名称的约定来标识受保护的属性。封装使我们能够控制属性的访问和修改,并提供统一的接口来与对象交互。

这里提及了受保护的属性,其实除了属性之外,方法同样也可以声明为保护方法和私有方法,二者之间是存在着一定的区别的。

保护属性

保护属性就是在属性名前面使用一个下划线(_)表示属性是保护的。保护属性可以被访问和修改,但它们被视为私有属性,应该在类的外部进行访问。

class Person:
    def __init__(self, name, age):
        self._name = name  # 保护属性
        self._age = age

    def speak(self):
        print(f"I am {self._name}, {self._age} years old.")

# 创建对象
person = Person("John", 25)

# 访问保护属性
print(person._name)   # 输出:John

# 修改保护属性
person._name = "Michael"
print(person._name)   # 输出:Michael

# 使用方法
person.speak()   # 输出:I am Michael, 25 years old.

在上面的示例中,我们使用_name_age作为保护属性。尽管它们具有保护属性的约定,仍然可以通过对象的实例访问和修改这些属性。person._name语法仍然是有效的,但作为开发者,我们应该将其视为不应直接访问的属性。

私有属性

在属性名前面使用两个下划线(__)表示属性是私有的。私有属性不能直接从类的外部访问和修改。

class Person:
    def __init__(self, name, age):
        self.__name = name  # 私有属性
        self.__age = age

    def speak(self):
        print(f"I am {self.__name}, {self.__age} years old.")

# 创建对象
person = Person("John", 25)

# 访问私有属性(错误示例)
print(person.__name)   # 报错:'Person' object has no attribute '__name'

# 使用方法
person.speak()   # 输出:I am John, 25 years old.

在上面的示例中,我们使用__name__age作为私有属性。私有属性不能直接通过对象的实例访问,因为Python会对它们进行名称修饰(name mangling)。如果尝试直接访问,会引发AttributeError错误。

为了在类的内部访问私有属性,可以使用名称修饰的属性名,以_类名作为前缀。例如,self.__name可以通过self._Person__name来访问。

总结来说,保护属性和私有属性都用于指示属性的访问权限。保护属性可以被访问和修改,但是被视为私有属性,应该在类的外部进行访问私有属性不能直接从类的外部访问,可以通过名称修饰的方式在类的内部访问。虽然Python并没有严格限制私有属性的访问,但是按照约定,我们应该将其视为不应直接访问的属性。

二、继承

继承是面向对象编程中的一种重要概念,它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和方法。

继承的主要优势在于代码的重用。通过继承,子类可以继承父类的属性和方法,并可以添加新的属性和方法,或覆盖(重写)父类的方法,从而实现代码的扩展和定制。

在Python中,可以通过以下方式来实现继承:

# 定义父类
class Animal:
    def __init__(self, name):
        self.name = name

    def sound(self):
        print("This is the sound of an animal.")

# 定义子类,继承父类
class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name)   # 调用父类的初始化方法
        self.color = color

    def sound(self):   # 重写父类的方法
        print("Meow!")

# 创建子类对象
cat = Cat("Tom", "black")

# 访问继承的属性
print(cat.name)   # 输出:Tom

# 调用继承的方法
cat.sound()   # 输出:Meow!

在上述示例中,我们首先定义了一个父类Animal,它具有name属性和sound()方法。然后,我们定义了一个子类Cat,并使用括号中的父类名称指明该子类继承自Animal

子类Cat具有自己的构造函数__init__(),它使用super()函数调用父类Animal的构造函数,以继承父类的属性name。子类还添加了自己的属性color

子类Cat还重写了父类Animal的方法sound()。当我们调用cat.sound()时,子类对象的方法会被执行,而不是父类的方法。这就是方法重写的概念,在子类中可以根据需要重新定义父类的方法实现。

继承的关键点:

  1. 子类通过在类定义中指定父类名称来继承父类。
  2. 子类继承了父类的属性和方法,并可以添加自己的属性和方法。
  3. 子类可以重写父类的方法,以实现自己的行为。

通过继承,可以实现多级继承(子类继承自其他子类)和多重继承(子类继承自多个父类)。继承在面向对象编程中扮演着重要的角色,它提高了代码的可重用性、扩展性和可维护性。

注:在Python中,一个子类可以继承多个父类

三、多态

多态(Polymorphism)是面向对象编程中的一个重要概念,它允许使用相同的接口来处理不同类型的对象,从而实现代码的灵活性和可扩展性。

多态性可以分为编译时多态(静态多态)和运行时多态(动态多态):

编译时多态:也称为静态多态,它发生在编译时期,通过函数的重载和运算符的重载来实现。编译器会根据函数或运算符的参数类型和数量,选择合适的函数或运算符来执行。

def add(a, b):
    return a + b

def add(a, b, c):
    return a + b + c

result1 = add(2, 3)
print(result1)   # 输出:5

result2 = add(2, 3, 4)
print(result2)   # 输出:9

在上述示例中,我们定义了两个具有相同函数名的函数add,但是它们具有不同的参数数量。当我们调用add函数时,编译器会根据传递的参数数量选择合适的函数实现。

运行时多态:也称为动态多态,它发生在运行时期,通过继承和方法重写来实现。在运行时,对象的实际类型决定了调用哪个方法。

class Animal:
    def sound(self):
        print("This is the sound of an animal.")

class Cat(Animal):
    def sound(self):
        print("Meow!")

class Dog(Animal):
    def sound(self):
        print("Woof!")

# 创建对象
cat = Cat()
dog = Dog()

# 调用方法
cat.sound()   # 输出:Meow!
dog.sound()   # 输出:Woof!

在上述示例中,我们定义了一个基类Animal,并创建了两个子类CatDog。这两个子类都重写了基类中的sound()方法。

当我们调用cat.sound()dog.sound()时,由于对象的实际类型是CatDog,所以会调用各自子类中的方法实现。这就是运行时多态,对象的实际类型决定了方法的调用。

多态提供了代码的灵活性和可扩展性。通过使用相同的接口处理不同类型的对象,我们可以将代码设计得更加通用,降低了代码的耦合度,并提高了代码的可维护性和可读性。

小结

关于面向对象,在上篇文章中说过其是一个很抽象的概念,所以希望大家能够结合文章去花费大量的时间来进行练习,通过代码来辅助理解其定义是一个不错的学习方式。

**练习:**现有一个图书管理系统,目前已实现的功能有用户借书与还书两个功能,请以空闲的时间来完成对该图书管理系统的功能,功能包括如下:用户注册、用户信息修改、用户删除、添加图书、图书信息修改、删除图书。除此外,也可以根据个人能力进行功能的添加。练习代码如需要审查的话可私信本人进行评改。

已实现的代码如下:

class User:
    """用户类"""
    def __init__(self, name):
        self.name = name

    def borrow_book(self, library, book):
        """借书"""
        library.remove_book(book)
        print(f"{self.name} borrowed the book: {book.title}")

    def return_book(self, library, book):
        """还书"""
        library.add_book(book)
        print(f"{self.name} returned the book: {book.title}")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

quanmoupy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值