当前位置: 首页 > news >正文

学习笔记:封装和单继承

一、封装

1. 什么是封装?

        封装是面向对象编程的三大特性之一(封装、继承、多态)。简单来说,封装就是将数据和操作数据的方法捆绑在一起,隐藏对象的内部细节,只对外提供必要的接口。

        想象一下:你的手机就是一个很好的封装例子。你不需要知道内部的电路如何工作,只需要通过屏幕、按键这些接口来操作它。

2. 为什么需要封装?

  • 保护数据,防止被意外修改
  • 隐藏实现细节,让使用者专注于如何使用
  • 提高代码的可维护性和安全性

3. 如何在 Python 中实现封装?

在 Python 中,我们通过类来实现封装,使用不同的访问修饰符来控制属性和方法的访问权限:

  • 公开属性 / 方法:默认,可在任何地方访问
  • 私有属性 / 方法:在名称前加两个下划线__,只能在类内部访问
  • 受保护属性 / 方法:在名称前加一个下划线_,表示仅供内部或子类使用(约定)

4. 封装示例代码

class Person:# 构造方法,初始化对象def __init__(self, name, age):self.name = name  # 公开属性self._height = 170  # 受保护属性(约定)self.__age = age  # 私有属性# 公开方法def greet(self):return f"Hello, my name is {self.name}, I'm {self.__get_age()} years old."# 私有方法def __get_age(self):return self.__age# 提供访问私有属性的接口def get_age(self):return self.__age# 提供修改私有属性的接口def set_age(self, new_age):if new_age > 0 and new_age < 120:self.__age = new_ageelse:print("Invalid age!")# 创建对象
person = Person("Alice", 25)# 访问公开属性和方法
print(person.name)  # 输出: Alice
print(person.greet())  # 输出: Hello, my name is Alice, I'm 25 years old.# 尝试访问私有属性(会报错)
# print(person.__age)  # 报错: AttributeError# 通过公开接口访问私有属性
print(person.get_age())  # 输出: 25# 通过公开接口修改私有属性
person.set_age(30)
print(person.get_age())  # 输出: 30# 尝试设置无效年龄
person.set_age(150)  # 输出: Invalid age!

5. 封装的使用场景

  • 当你希望某些数据只能通过特定方式修改时(如上面例子中的年龄验证)
  • 当类的内部实现可能会变化,但你希望保持对外接口不变时
  • 当你想隐藏复杂的实现细节,只提供简单的使用方式时

二、单继承

1. 什么是继承?

        继承是指一个类(子类)可以继承另一个类(父类)的属性和方法,同时可以添加自己特有的属性和方法。

        单继承就是一个子类只继承一个父类。

        想象一下:猫和狗都是动物,它们都有名字、会叫、会跑,但猫会抓老鼠,狗会看门。这里 "动物" 就是父类,"猫" 和 "狗" 是子类。

2. 为什么需要继承?

  • 代码复用:避免重复编写相同的代码
  • 建立类之间的关系,使代码结构更清晰
  • 便于扩展:在不修改父类的情况下添加新功能

3. 如何在 Python 中实现单继承?

在定义类时,在类名后的括号中指定要继承的父类。

class 子类名(父类名):# 子类的属性和方法

4. 继承示例代码

# 父类
class Animal:def __init__(self, name):self.name = namedef eat(self):print(f"{self.name} is eating.")def sleep(self):print(f"{self.name} is sleeping.")# 子类,继承自Animal
class Dog(Animal):# 子类可以添加自己的方法def bark(self):print(f"{self.name} is barking: Woof! Woof!")# 子类可以重写父类的方法def sleep(self):print(f"{self.name} is sleeping in a doghouse.")# 子类,继承自Animal
class Cat(Animal):def meow(self):print(f"{self.name} is meowing: Meow! Meow!")def sleep(self):print(f"{self.name} is sleeping on the sofa.")# 创建对象
dog = Dog("Buddy")
cat = Cat("Mittens")# 调用继承的方法
dog.eat()  # 输出: Buddy is eating.
cat.eat()  # 输出: Mittens is eating.# 调用子类自己的方法
dog.bark()  # 输出: Buddy is barking: Woof! Woof!
cat.meow()  # 输出: Mittens is meowing: Meow! Meow!# 调用重写的方法
dog.sleep()  # 输出: Buddy is sleeping in a doghouse.
cat.sleep()  # 输出: Mittens is sleeping on the sofa.

5. 继承中的 super () 函数

super()函数用于调用父类的方法,通常用于在子类中扩展父类的方法。

class Animal:def __init__(self, name, color):self.name = nameself.color = colorclass Bird(Animal):def __init__(self, name, color, can_fly):# 调用父类的__init__方法super().__init__(name, color)# 添加子类自己的属性self.can_fly = can_flydef introduce(self):flying_status = "can fly" if self.can_fly else "can't fly"return f"I'm {self.name}, a {self.color} bird that {flying_status}."# 创建对象
sparrow = Bird("Sparrow", "brown", True)
penguin = Bird("Penguin", "black and white", False)print(sparrow.introduce())  # 输出: I'm Sparrow, a brown bird that can fly.
print(penguin.introduce())  # 输出: I'm Penguin, a black and white bird that can't fly.

6. 单继承的使用场景

  • 当多个类有共同的属性和方法时,可以将这些共同部分提取到父类中
  • 当需要创建一个更具体的类,它是某个更通用类的特例时
  • 当需要扩展现有类的功能,但又不想修改原有类的代码时(开放 - 封闭原则)

三、封装与继承的结合使用

在实际开发中,封装和继承通常是结合使用的。子类可以继承父类的公有和受保护成员,但不能直接访问父类的私有成员。

class Person:def __init__(self, name, age):self.name = name  # 公开属性self.__age = age  # 私有属性def get_age(self):return self.__agedef set_age(self, new_age):if new_age > 0 and new_age < 120:self.__age = new_ageelse:print("Invalid age!")# 学生类继承自人类
class Student(Person):def __init__(self, name, age, student_id):super().__init__(name, age)self.student_id = student_iddef introduce(self):return f"I'm {self.name}, a student with ID {self.student_id}, {self.get_age()} years old."# 创建学生对象
student = Student("Bob", 20, "S12345")
print(student.introduce())  # 输出: I'm Bob, a student with ID S12345, 20 years old.# 尝试直接访问父类的私有属性(会报错)
# print(student.__age)  # 报错: AttributeError# 通过父类提供的接口访问
print(student.get_age())  # 输出: 20

四、访问私有属性和方法

1、访问私有属性和方法的方式

        Python 中,私有属性和方法是通过在名称前加两个下划线__来定义的。这种命名会触发 "名称修饰"(name mangling)机制,即解释器会将其重命名为_类名__属性名的形式,以避免子类中的命名冲突。

        因此,我们可以通过以下两种方式访问私有成员:

1. 通过类内部的公有方法访问(推荐)

        这是最规范的方式,通过类内部定义的公开方法(getter/setter)来间接访问或修改私有成员,符合封装的设计思想。

class Person:def __init__(self, name, age):self.__name = name  # 私有属性self.__age = age    # 私有属性# 公开方法:获取私有属性def get_name(self):return self.__name# 公开方法:修改私有属性(可添加验证逻辑)def set_age(self, new_age):if 0 < new_age < 120:self.__age = new_ageelse:print("年龄必须在0-120之间")# 公开方法:调用私有方法def show_info(self):return self.__get_info()  # 内部调用私有方法# 私有方法def __get_info(self):return f"姓名:{self.__name},年龄:{self.__age}"# 使用示例
p = Person("张三", 25)# 通过公有方法访问私有属性
print(p.get_name())  # 输出:张三# 通过公有方法修改私有属性
p.set_age(30)
print(p.show_info())  # 输出:姓名:张三,年龄:30# 通过公有方法调用私有方法(间接)
print(p.show_info())  # 输出:姓名:张三,年龄:30

2. 通过名称修饰后的名称直接访问(不推荐)

        利用 Python 的名称修饰规则,我们可以在类外部通过_类名__私有成员名的形式直接访问私有属性和方法。但这种方式破坏了封装性,不建议在实际开发中使用。

class Person:def __init__(self, name):self.__name = name  # 私有属性def __say_hello(self):  # 私有方法return f"你好,我是{self.__name}"# 使用示例
p = Person("李四")# 直接访问私有属性(名称修饰后)
print(p._Person__name)  # 输出:李四# 直接调用私有方法(名称修饰后)
print(p._Person__say_hello())  # 输出:你好,我是李四

2、注意事项

  1. 不推荐直接访问
    私有成员的设计初衷是隐藏内部实现,直接通过名称修饰访问会破坏封装性,导致代码耦合度升高,难以维护。

  2. 命名约定的意义
    Python 的私有机制更多是一种 "约定" 而非强制限制,目的是提醒开发者:"这些成员是内部使用的,外部不应直接修改"。

  3. 子类无法直接继承私有成员
    子类不能直接访问父类的私有成员,即使通过名称修饰,也需要使用父类的类名(如_父类名__私有成员)才能访问,这进一步保证了父类内部实现的安全性。

3、总结

方式语法推荐度特点
公有方法访问类内部定义get_xxx()/set_xxx() 推荐符合封装思想,可控制访问逻辑
名称修饰访问_类名__私有成员名 不推荐破坏封装,仅用于特殊调试场景

五、知识点总结

封装

  1. 封装是将数据和操作数据的方法捆绑在一起
  2. 目的是保护数据、隐藏细节、提高可维护性
  3. Python 中通过类实现封装,使用__定义私有成员,_定义受保护成员
  4. 通常为私有成员提供公开的 getter 和 setter 方法来访问和修改

单继承

  1. 继承是让一个类获得另一个类的属性和方法
  2. 单继承指一个子类只继承一个父类
  3. 目的是代码复用、建立类关系、便于扩展
  4. 使用class 子类名(父类名)语法实现继承
  5. 使用super()函数调用父类的方法
  6. 子类可以重写父类的方法,实现多态

综合

  1. 封装和继承通常结合使用
  2. 子类可以继承父类的公有和受保护成员
  3. 子类不能直接访问父类的私有成员,需通过父类提供的接口访问
http://www.dtcms.com/a/309308.html

相关文章:

  • ls hgfs提示ls: cannot access ‘hgfs‘: Permission denied
  • Spring Boot 2.1.18 集成 Elasticsearch 6.6.2 实战指南
  • OneCode3.0 DSM:领域驱动设计驱动下的自定义枚举领域划分实践
  • CMake Debug/Release配置生成器表达式解析
  • 加密与安全
  • ACM SIGCOMM 2024论文精选-01:5G【Prism5G】
  • 让 OAuth 授权码流程更安全的 PKCE 技术详解
  • Unity相机控制
  • C#线程同步(三)线程安全
  • LT3045EDD#TRPBF ADI亚德诺半导体 线性稳压器 电源管理应用设计
  • PCB 控深槽如何破解 5G 基站 120℃高热魔咒?
  • Webhook是什么
  • 【Nginx反向代理】通过Nginx反向代理将多个后端server统一到同一个端口上的方法
  • 开源爬虫管理工具
  • [ Leetcode ]---快乐数
  • 【Redis】key的设计格式
  • Django模型查询与性能调优:告别N+1问题
  • 第三篇:几何体入门:内置几何体全解析
  • 【LLM】讲清楚MLA原理
  • Linux(15)——进程间通信
  • EasyExcel 公式计算大全
  • Spring Boot Actuator 保姆级教程
  • 包裹移动识别误报率↓76%:陌讯时序建模算法实战解析
  • C#实现左侧折叠导航菜单
  • 数据结构(9)栈和队列
  • 完整的 Spring Boot + Hibernate/JPA + P6Spy 配置指南
  • 凸优化:常见的优化问题,偏统计视角
  • cesium FBO(四)自定义相机渲染到Canvas(离屏渲染)
  • android APT技术
  • 今日链表系列