学习笔记:封装和单继承

一、封装

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. 子类不能直接访问父类的私有成员,需通过父类提供的接口访问
相关推荐
kfepiza4 分钟前
vim的`:q!` 与 `ZQ` 笔记250729
linux·笔记·编辑器·vim
三小尛8 分钟前
C++赋值运算符重载
开发语言·c++
籍籍川草11 分钟前
JVM指针压缩的那些事
java·开发语言·jvm
小徐不徐说18 分钟前
C++ 模板与 STL 基础入门:从泛型编程到实战工具集
开发语言·数据结构·c++·qt·面试
艾莉丝努力练剑20 分钟前
【C/C++】类和对象(上):(一)类和结构体,命名规范——两大规范,新的作用域——类域
java·c语言·开发语言·c++·学习·算法
AndrewHZ32 分钟前
【图像处理基石】如何对遥感图像进行实例分割?
图像处理·人工智能·python·大模型·实例分割·detectron2·遥感图像分割
froginwe1136 分钟前
WebPages PHP:深入解析PHP在网页开发中的应用
开发语言
No0d1es1 小时前
第13届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2021年11月27日真题
python·青少年编程·蓝桥杯·选拔赛
Emotion亦楠1 小时前
Java 学习笔记:常用类、String 与日期时间处理
java·笔记·学习
天天找自己1 小时前
精通分类:解析Scikit-learn中的KNN、朴素贝叶斯与决策树(含随机森林)
python·决策树·机器学习·分类·scikit-learn