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

Python 入门教程(7)面向对象 | 7.5、继承

文章目录

  • 一、继承
    • 1、如何实现继承?
    • 2、派生类如何访问基类成员?
      • 2.1、直接访问
      • 2.2、使用`super()`
      • 2.3、访问基类的类属性或类方法
      • 2.4、派生类不能访问父类的私有方法、成员
    • 3、构造函数执行顺序
    • 4、析构函数执行顺序
    • 5、继承的类型
    • 6、总结

前言:

Python中的继承是面向对象编程(OOP)中的一个重要概念,它允许一个类(子类)继承另一个类(父类)的属性和方法,以下是关于Python继承的详细介绍。

一、继承

1、如何实现继承?

在Python中,使用冒号:来指定继承关系。子类名后紧跟一个或多个基类名,基类名之间用逗号,分隔。

class Parent:  def __init__(self, name):  self.name = name  def greet(self):  print(f"Hello, my name is {self.name}")  class Child(Parent):  def __init__(self, name, age):  super().__init__(name)  # 调用父类的__init__方法  self.age = age  def greet(self):  super().greet()  # 调用父类的greet方法  print(f"And I am {self.age} years old.")  # 使用子类  
child = Child("Alice", 10)  
child.greet()  # 输出: Hello, my name is Alice  #       And I am 10 years old.

2、派生类如何访问基类成员?

2.1、直接访问

如果基类中的成员(属性或方法)没有被重写(即派生类中没有定义同名的成员),那么派生类的实例可以直接访问这些成员,就像它们是派生类自己的成员一样。

class Base:  def __init__(self):  self.base_attr = "I'm from Base"  def base_method(self):  print("This is a method from Base")  class Derived(Base):  def __init__(self):  super().__init__()  # 确保基类的__init__被调用  self.derived_attr = "I'm from Derived"  # 可以在这里重写基类的方法,或者添加新的方法  # 创建Derived类的实例  
obj = Derived()  # 访问基类的属性  
print(obj.base_attr)  # 输出: I'm from Base  # 调用基类的方法  
obj.base_method()  # 输出: This is a method from Base

2.2、使用super()

如果派生类重写了基类的方法,并且你需要在派生类的方法内部调用基类的方法,那么可以使用super()函数。super()返回了一个代表父类的临时对象,允许你调用那个类的方法。

class Base:  def method(self):  print("Base method")  class Derived(Base):  def method(self):  super().method()  # 调用基类的method方法  print("Derived method")  # 创建Derived类的实例并调用method  
obj = Derived()  
obj.method()  # 首先输出: Base method,然后输出: Derived method

2.3、访问基类的类属性或类方法

如果基类中的成员是类属性(即直接定义在类级别而不是实例级别的属性)或类方法(使用@classmethod装饰器定义的方法),那么你可以通过基类名来访问它们,就像访问任何模块级别的变量或函数一样。但是,更常见的做法是通过实例来访问,因为即使它们是类级别的,也可以通过实例来访问。

class Base:  class_attr = "Class attribute from Base"  @classmethod  def class_method(cls):  print("Class method from Base")  class Derived(Base):  pass  # 通过基类名访问类属性和类方法  
print(Base.class_attr)  # 输出: Class attribute from Base  
Base.class_method()     # 输出: Class method from Base  # 通过派生类实例访问(同样有效)  
obj = Derived()  
print(obj.class_attr)   # 输出: Class attribute from Base  
obj.class_method()      # 输出: Class method from Base

2.4、派生类不能访问父类的私有方法、成员

在Python中,从语法和编码实践的角度来看,子类不能直接访问父类的私有方法和私有成员(以双下划线__开头的成员)。这是因为Python通过名字重整来保护这些私有成员,使其在子类中不能直接通过原始名字访问。

3、构造函数执行顺序

在Python中,当创建派生类(子类)的实例时,构造函数的执行顺序遵循一定的规则。这些规则确保了基类(父类)在派生类之前被正确地初始化。以下是构造函数执行顺序的详细说明:

  • 派生类不会自动执行基类的构造函数: 当创建派生类的实例时,需要显式地调用基类的构造函数来完成基类部分的初始化。如果派生类中未显式调用基类的构造函数,且基类没有默认构造函数(即不带参数的构造函数),则会导致错误。
  • 基类构造函数的调用是可选的: 虽然通常推荐在派生类构造函数中调用基类的构造函数来确保基类被正确初始化,但这并不是强制的。如果派生类不需要基类中的任何初始化逻辑,那么它可以省略对基类构造函数的调用。然而,这样做可能会导致基类中的某些属性未被初始化,从而引发问题。

4、析构函数执行顺序

析构函数的调用发生在对象的生命周期结束时,即当对象的所有引用都被删除,Python的垃圾收集器准备回收对象时。但是,需要注意的是,析构函数的调用顺序与构造函数的调用顺序相反,但这个过程并不完全由程序员控制,因为它依赖于垃圾收集器的行为

class Base:  def __init__(self):  print("Base __init__")  def __del__(self):  print("Base __del__")  class Derived(Base):  def __init__(self):  super().__init__()  # 显式调用基类的构造函数  print("Derived __init__")  def __del__(self):  print("Derived __del__")  super().__del__()  # 注意:通常不这样做,因为析构函数不是用来被显式调用的  # 创建Derived类的实例  
obj = Derived()  # ...(在这里,对象obj被使用)  # 当obj的引用被删除,且垃圾收集器决定回收对象时,析构函数将被调用  
# 但由于析构函数的调用时机不确定,这里不会显示输出

注意: 由于Python的垃圾收集器是自动的,并且析构函数的调用时机是不确定的(即依赖于垃圾收集器的具体实现和内存使用情况),因此通常不建议在析构函数中执行重要的清理工作,而应该使用其他机制来确保资源被及时释放。

5、继承的类型

Python同时支持单继承与多继承

  • 单继承: 子类只继承一个父类,这是最常见的继承方式。
  • 多继承: 子类继承多个父类。Python支持多继承,但使用时需要谨慎,以避免复杂的类关系和不可预测的行为。
class Parent1:  def method1(self):  print("This is method1 from Parent1")  class Parent2:  def method2(self):  print("This is method2 from Parent2")  class Child(Parent1, Parent2):  pass  # 多继承示例  
child = Child()  
child.method1()  # 输出: This is method1 from Parent1  
child.method2()  # 输出: This is method2 from Parent2

6、总结

通过继承和面向对象的其他特性(如封装、多态性),Python提供了一套强大的工具,使得开发者能够编写出更加模块化、易于维护和扩展的代码。


http://www.mrgr.cn/news/35463.html

相关文章:

  • JS 实现游戏流畅移动与按键立即响应
  • 速盾:cdn 支持 php 吗?
  • spi 回环
  • Ubuntu22.04 安装mysql8 无法修改端口及配置的问题 坑啊~~~~
  • Vue前端开发,组件及组件的使用
  • 真正的一站式视频出海解决方案
  • Docker部署服务:快速入门指南
  • opencv学习笔记(一)
  • Vue3——Vite篇
  • rmdir :删除空文件夹
  • Stable Diffusion绘画 | XYZ Plot:让对比一目了然
  • 优青博导团队指导-组蛋白甲基化修饰、实验设计、实验结果分析、测序分析及SCI论文辅助,精准高效,为农医学科研保驾护航!
  • 前端——阿里图标的使用
  • USB 电缆中的信号线 DP、DM 的缩写由来
  • 8086的指令系统
  • 物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总
  • ESXI主机加入VCENTER现有集群提示出现常规性错误
  • Python【修炼1】
  • LOGO设计新革命:5款AI工具让你秒变设计大师(必藏)
  • Java高级Day50-连接池
  • 深入解析:Kubernetes 如何使用 etcd 作为配置中心和注册中心
  • PHP 递归遍历目录
  • JUC并发编程_四大函数式接口和 Stream 流式计算
  • JetBrains系列产品无限重置免费试用方法
  • 35岁程序员转行大模型岗位:详细学习路线,从零基础到精通2024最新
  • input文本框随其中内容而变化长