学习笔记:封装和单继承

一、封装

1. 什么是封装?

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

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

2. 为什么需要封装?

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

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

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

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

4. 封装示例代码

python 复制代码
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_age
        else:
            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 中实现单继承?

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

python 复制代码
class 子类名(父类名):
    # 子类的属性和方法

4. 继承示例代码

python 复制代码
# 父类
class Animal:
    def __init__(self, name):
        self.name = name
        
    def 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()函数用于调用父类的方法,通常用于在子类中扩展父类的方法。

python 复制代码
class Animal:
    def __init__(self, name, color):
        self.name = name
        self.color = color

class Bird(Animal):
    def __init__(self, name, color, can_fly):
        # 调用父类的__init__方法
        super().__init__(name, color)
        # 添加子类自己的属性
        self.can_fly = can_fly
        
    def 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. 单继承的使用场景

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

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

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

python 复制代码
class Person:
    def __init__(self, name, age):
        self.name = name  # 公开属性
        self.__age = 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_age
        else:
            print("Invalid age!")

# 学生类继承自人类
class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id
        
    def 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)来间接访问或修改私有成员,符合封装的设计思想。

python 复制代码
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_age
        else:
            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
  1. 通过名称修饰后的名称直接访问(不推荐)

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

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. 子类不能直接访问父类的私有成员,需通过父类提供的接口访问
相关推荐
hui函数2 小时前
Flask电影投票系统全解析
后端·python·flask
壹Y.3 小时前
非线性规划学习笔记
学习·数学建模
好学且牛逼的马3 小时前
GOLANG 接口
开发语言·golang
ahauedu3 小时前
AI资深 Java 研发专家系统解析Java 中常见的 Queue实现类
java·开发语言·中间件
韭菜钟3 小时前
在Qt中用cmake实现类似pri文件的功能
开发语言·qt·系统架构
暗流者3 小时前
AAA 服务器与 RADIUS 协议笔记
运维·服务器·笔记
项目題供诗3 小时前
React学习(十二)
javascript·学习·react.js
闲人编程3 小时前
Python第三方库IPFS-API使用详解:构建去中心化应用的完整指南
开发语言·python·去中心化·内存·寻址·存储·ipfs
计算机编程小咖4 小时前
《基于大数据的农产品交易数据分析与可视化系统》选题不当,毕业答辩可能直接挂科
java·大数据·hadoop·python·数据挖掘·数据分析·spark
艾莉丝努力练剑4 小时前
【C语言16天强化训练】从基础入门到进阶:Day 7
java·c语言·学习·算法