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

详解 Python 中的面向对象编程(2)

alt

引言

面向对象编程(OOP)是一种编程范式,它通过将属性和行为整合到对象中来构建程序。本教程将带你了解Python语言中面向对象编程的基本概念。

想象一下,对象就像是系统中的各个部件。可以把程序比作一条工厂流水线。在流水线的每一个环节,部件都会对材料进行处理,最终将原材料变成成品。

对象内部存储着数据,类似于流水线上各个环节所需的原材料或经过初步处理的材料。同时,对象还具有行为,即流水线上每个部件执行的具体操作。

通过本教程[1],你将学会:

  • 如何定义一个类,这可以看作是创建对象的模板。
  • 如何利用类来生成新的对象。
  • 如何利用类继承来构建和模拟复杂的系统。

如何继承另一个类?

继承是一种机制,允许一个类获得另一个类的属性和方法。通过这种方式形成的新类称为子类,而作为继承基础的类则称为父类。

继承父类是通过定义一个新类,并在新类的声明中将父类的名称放在括号内来实现的。

# inheritance.py

class Parent:
    hair_color = "brown"

class Child(Parent):
    pass

在这个简化的示例中,子类 Child 继承了父类 Parent 的特性。由于子类自动继承了父类的属性和方法,所以 Child 的 hair_color 属性也会是 "brown",无需你明确指定。

子类不仅可以继承父类的所有属性和方法,还可以重写或扩展它们,以形成自己独特的特性和行为。

虽然这个比喻不是完全准确,但你可以将对象的继承想象成遗传学中的遗传。比如,你的发色可能是从父母那里遗传来的,这是你出生时就确定的属性。但如果某天你决定将头发染成紫色,那么在这个属性上,你就相当于覆盖了从父母那里遗传来的特征。

# inheritance.py

class Parent:
    hair_color = "brown"

class Child(Parent):
    hair_color = "purple"

如果你对代码示例做出这样的修改,那么 Child 类的 hair_color 属性值将变为 "purple"。

在某种程度上,你也继承了父母的语言。如果你的父母说英语,你自然也会说英语。设想你决定学习第二语言,例如德语。这样,你就扩展了自己的属性集,因为你添加了一个你的父母所不具备的新属性:

# inheritance.py

class Parent:
    speaks = ["English"]

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.speaks.append("German")

你将在后续章节中更深入地了解上述代码的运作机制。但在深入探讨Python的继承概念之前,我们先去一个狗公园散步,这有助于你更好地理解在自己的代码中使用继承的原因。

  • 示例:狗公园

想象一下,你现在身处一个狗公园。这里聚集了各种不同品种的狗,它们各自展示着不同的行为。

假设你想用Python类来构建一个狗公园的模型。在上一节中你编写的Dog类能够根据名字和年龄来区分不同的狗,但还无法根据品种进行区分。

你可以通过在编辑器窗口中为Dog类添加一个.breed属性来对其进行修改:

# dog.py

class Dog:
    species = "Canis familiaris"

    def __init__(self, name, age, breed):
        self.name = name
        self.age = age
        self.breed = breed

    def __str__(self):
        return f"{self.name} is {self.age} years old"

    def speak(self, sound):
        return f"{self.name} says {sound}"

按下F5键来保存你的文件。接下来,你可以在交互式窗口中创建多种不同品种的狗,以此来构建一个狗公园的模型:

>>> miles = Dog("Miles"4"Jack Russell Terrier")
>>> buddy = Dog("Buddy"9"Dachshund")
>>> jack = Dog("Jack"3"Bulldog")
>>> jim = Dog("Jim"5"Bulldog")

不同品种的狗有着各自独特的行为特征。比如,斗牛犬发出的低沉吠声听起来像是“汪汪”,而腊肠犬则发出更尖锐的“啾啾”声。

如果只使用Dog类,每次调用Dog实例的.speak()方法时,你都需要为sound参数指定一个具体的叫声字符串:

>>> buddy.speak("Yap")
'Buddy says Yap'

>>> jim.speak("Woof")
'Jim says Woof'

>>> jack.speak("Woof")
'Jack says Woof'

反复为.speak()方法传递字符串不仅繁琐,也缺乏便捷性。更合理的设计是让.breed属性自动决定每个Dog实例的叫声,但在当前情况下,你每次都需要手动为.speak()方法指定正确的字符串。

为了改善使用Dog类的体验,你可以通过为每种狗的品种创建一个子类来实现。这样,你不仅可以扩展每个子类继承的功能,还可以为.speak()方法设置一个默认的叫声参数。

  • 父类与子类

在接下来的部分,你将为前文提到的三种狗的品种——杰克罗素梗、腊肠犬和斗牛犬——各创建一个子类。

以下是你目前所使用Dog类的完整定义,供参考:

# dog.py

class Dog:
    species = "Canis familiaris"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name} is {self.age} years old"

    def speak(self, sound):
        return f"{self.name} says {sound}"

在上一节完成了狗公园的示例之后,你已经去除了.breed属性。接下来,你将使用子类的方式来记录狗的品种信息。

创建子类的过程是,你首先定义一个具有独立名称的新类,然后在其后添加父类的名称,并用括号括起来。在dog.py文件中添加以下代码,以便创建Dog类的三个新的子类:

# dog.py

# ...

class JackRussellTerrier(Dog):
    pass

class Dachshund(Dog):
    pass

class Bulldog(Dog):
    pass

按下F5键保存并执行文件。定义好子类之后,你就可以在交互式窗口里创建几种不同品种的狗了:

>>> miles = JackRussellTerrier("Miles"4)
>>> buddy = Dachshund("Buddy"9)
>>> jack = Bulldog("Jack"3)
>>> jim = Bulldog("Jim"5)

子类的对象会继承其父类的所有特性和方法。

>>> miles.species
'Canis familiaris'

>>> buddy.name
'Buddy'

>>> print(jack)
Jack is 3 years old

>>> jim.speak("Woof")
'Jim says Woof'

要识别一个特定对象属于哪个类,你可以利用Python内置的type()函数来查询:

>>> type(miles)
<class '__main__.JackRussellTerrier'>

如果你想判断miles是否属于Dog类,可以使用内置的isinstance()函数进行判断:

>>> isinstance(miles, Dog)
True

isinstance()函数需要两个参数:一个对象和一个类。如上例所示,isinstance()用来判断miles是否是Dog类的实例,结果返回True。

>>> isinstance(miles, Bulldog)
False

>>> isinstance(jack, Dachshund)
False

miles、buddy、jack和jim这些对象都是Dog类的实例。但是,miles并不是Bulldog类的实例,同样,jack也不是Dachshund类的实例。

# dog.py

# ...

class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
        return f"{self.name} says {sound}"

# ...

通常来说,所有通过子类创建的对象都被视为父类的实例,尽管它们可能并不属于其他子类的实例。

既然你已经为一些不同品种的狗定义了子类,现在你可以为每个品种的狗指定它们特有的叫声。

  • 扩展父类功能

因为不同品种的狗叫声略有不同,你可能会想要为它们的.speak()方法的sound参数设定一个默认值。这需要你在每个品种的类定义中重写.speak()方法。

重写父类中定义的方法,就是在子类中定义一个相同名称的方法。以下展示了如何在JackRussellTerrier类中进行这样的操作:

# dog.py

# ...

class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
        return f"{self.name} says {sound}"

# ...

.speak()方法已经在JackRussellTerrier类中被重新定义,其sound参数的默认值被设定为“Arf”。

更新dog.py文件,加入新定义的JackRussellTerrier类,并按下F5键来保存并执行文件。此后,你可以直接在JackRussellTerrier的实例上调用.speak()方法,无需再为sound参数提供任何值。

>>> miles = JackRussellTerrier("Miles"4)
>>> miles.speak()
'Miles says Arf'

狗狗有时会发出各种各样的声音。比如,如果Miles生气了,开始咆哮,你依然可以通过.speak()方法传入“Grrr”这样的不同声音来表达:

>>> miles.speak("Grrr")
'Miles says Grrr'

关于类继承的一个重要概念是,对父类所做的更改会自动影响到子类,前提是子类没有重写被更改的属性或方法。

举个例子,如果你在编辑器中修改了Dog类中.speak()方法的返回字符串:

# dog.py

class Dog:
    # ...

    def speak(self, sound):
        return f"{self.name} barks: {sound}"

# ...

保存并运行文件(按F5)。此时,如果你创建了一个新的Bulldog实例,比如命名为jim,调用jim.speak()将返回新的字符串格式:

>>> jim = Bulldog("Jim"5)
>>> jim.speak("Woof")
'Jim barks: Woof'

但是,如果你在JackRussellTerrier实例上调用.speak(),输出的格式将不会按照Dog类的更新而改变:

>>> miles = JackRussellTerrier("Miles"4)
>>> miles.speak()
'Miles says Arf'

有时候,我们可能需要完全重写父类中的某个方法。但在这种情况下,我们希望JackRussellTerrier类能够保留对Dog类.speak()方法输出格式可能做出的任何更改。

为此,你需要在JackRussellTerrier子类中定义一个.speak()方法。与其明确指定输出字符串,不如在子类的.speak()方法内部,使用传递给JackRussellTerrier.speak()的相同参数,调用父类Dog的.speak()方法。

你可以通过super()函数来访问子类方法中的父类:

# dog.py

# ...

class JackRussellTerrier(Dog):
    def speak(self, sound="Arf"):
        return super().speak(sound)

# ...

当你在JackRussellTerrier类中调用super().speak(sound)时,Python会在Dog类中查找.speak()方法,并使用你提供的声音参数调用它。

更新dog.py文件,加入修改后的JackRussellTerrier类。保存并运行(按F5),然后在交互式窗口中测试新的实现:

>>> miles = JackRussellTerrier("Miles"4)
>>> miles.speak()
'Miles barks: Arf'

现在,当你调用miles.speak()时,输出的格式将与Dog类中更新后的格式保持一致。

总结

本教程向你介绍了Python中的面向对象编程(OOP)概念。像Java、C#和C++这样的现代编程语言都采用了OOP原则,所以你在这里学到的知识将对你未来的编程道路大有裨益。

通过本教程,你学会了:

  • 如何定义一个类,它作为创建对象的模板
  • 如何通过类的实例化来生成具体的对象
  • 利用属性和方法来确定对象的特性和行为
  • 利用继承机制,从一个父类派生出多个子类
  • 使用super()来调用父类中的方法
  • 通过isinstance()函数来判断一个对象是否基于另一个类进行扩展
Reference
[1]

Source: https://realpython.com/python3-object-oriented-programming/

本文由 mdnice 多平台发布

相关文章:

详解 Python 中的面向对象编程(2)

引言 面向对象编程&#xff08;OOP&#xff09;是一种编程范式&#xff0c;它通过将属性和行为整合到对象中来构建程序。本教程将带你了解Python语言中面向对象编程的基本概念。 想象一下&#xff0c;对象就像是系统中的各个部件。可以把程序比作一条工厂流水线。在流水线的每一…...

数据结构-线性表-顺序表

一. 了解顺序表 顺序表定义&#xff1a; 顺序表&#xff08;也称为线性数组&#xff09;是一种线性数据结构&#xff0c;它将数据元素按顺序存储在一块连续的内存空间中。顺序表的基本特征包括&#xff1a; 元素的顺序性&#xff1a;顺序表中的元素具有线性关系&#xff0c;每…...

AI绘画大模型-StableDiffusion最强模型sd3(本地安装方法)

​ 前言/introduction Stable Diffusion 3&#xff08;简称SD3&#xff09;是Stability AI最新推出的文本到图像生成模型。相比前代模型&#xff0c;SD3在生成质量、细节表现以及运行效率上有了显著提升&#xff0c;尤其在细腻的图像渲染和复杂的场景构建方面表现出色。SD3模型…...

SpringBoot调用外部接口的几种方式

SpringBoot调用外部接口的几种方式 使用FeignClient调用1、在使用方引入依赖2、服务接口调用方2.1、在启动类上加上EnableFeigncliens注解2.2、编写Feign接口调用服务controller层2.3、服务接口调用service层 3、服务接口提供者4、说明 使用RestTemplate调用1、引入依赖2、Rest…...

MySQL:information_schema查找某个表的主键是否在数据的其他位置出现之二

上一篇&#xff1a; MySQL&#xff1a;information_schema查找某个表的主键是否在数据的其他位置出现之一-CSDN博客 摘要 遍历数据库每一张表的每一个字段&#xff0c;是否存在字符串search_term 正文 源码 import pymysql from datetime import datetime# 测试函数 if __n…...

Linux进程和计划任务管理

目录 一、进程基本概念 1.进程 2.程序和进程的关系 二、查看进程信息 1.ps命令 1.1 ps aux命令 1.2 ps -elf 命令 2. top 命令 3.pgrep 命令 4.jobs 命令 三、查看进程树 四、进程的启动方式 1.手动启动 2.调度启动 五、终止进程的运行 1.CtrlC组合键 2.kill、kil…...

【Angular18】封装自定义组件

1. 准备组件 2. 创建打包文件夹及部分配置文件 创建 文件夹app-legalentities-root拷贝组件源文件到新的文件夹app-legalentities中创建文件 .npmrc registry发布地址always-authtrue创建文件 ng-package.json {"$schema": "./node_modules/ng-packagr/ng-pac…...

【流媒体】RTMPDump—RTMP_ConnectStream(创建流连接)

目录 1. RTMP_ConnectStream函数1.1 读取packet&#xff08;RTMP_ReadPacket&#xff09;1.2 解析packet&#xff08;RTMP_ClientPacket&#xff09;1.2.1 设置Chunk Size&#xff08;HandleChangeChunkSize&#xff09;1.2.2 用户控制信息&#xff08;HandleCtrl&#xff09;1…...

MySQL学习3之锁机制

一、什么是锁粒度&#xff1f; 锁粒度&#xff08;Lock Granularity&#xff09;是指在数据库中锁定数据资源的最小单位。锁粒度决定了锁定操作的范围&#xff0c;即锁定的是整个数据库、整个表、表中的某个分区、表中的某一页还是表中的某一行。 在MySQL中常见的锁粒度有&am…...

2004-2023年上市公司过度负债数据(含原始数据+计算结果)

2004-2023年上市公司过度负债数据&#xff08;含原始数据计算结果&#xff09; 1、时间&#xff1a;2004-2023年 2、来源&#xff1a;上市公司年报 3、指标&#xff1a;证券代码、证券简称、统计截止日期、是否剔除ST或*ST股、是否剔除当年新上市、已经退市或被暂停退市的公…...

[机器学习]--KNN算法(K邻近算法)

KNN (K-Nearest Neihbor,KNN)K近邻是机器学习算法中理论最简单,最好理解的算法,是一个 非常适合入门的算法,拥有如下特性: 思想极度简单,应用数学知识少(近乎为零),对于很多不擅长数学的小伙伴十分友好虽然算法简单,但效果也不错 KNN算法原理 上图是每一个点都是一个肿瘤病例…...

跨平台控制神器Escrcpy,您的智能生活助手

Escrcpy 是一款基于 Scrcpy 开发的图形化安卓手机投屏控制软件&#xff0c;它允许用户将 Android 手机屏幕实时镜像到电脑上&#xff0c;并使用电脑的鼠标和键盘直接操作手机&#xff0c;实现了无线且高效的操控。这款软件是免费开源的&#xff0c;支持跨平台使用&#xff0c;包…...

AR 眼镜之-开关机定制-实现方案

目录 &#x1f4c2; 前言 AR 眼镜系统版本 开关机定制 1. &#x1f531; 技术方案 1.1 技术方案概述 1.2 实现方案 1&#xff09;开机 Logo 2&#xff09;开机音效 3&#xff09;开机动画 4&#xff09;关机动画 5&#xff09;关机弹窗 2. &#x1f4a0; 开机 Logo…...

论文阅读-Transformer Layers as Painters

1. 摘要 尽管大语言模型现在已经被广泛的应用于各种任务&#xff0c;但是目前对其并没有一个很好的认知。为了弄清楚删除和重组预训练模型不同层的影响&#xff0c;本文设计了一系列的实验。通过实验表明&#xff0c;预训练语言模型中的lower和final layers与中间层分布不一致…...

【STL】vector模拟实现

vector引入 ​​ vector的实现主要依靠三个成员变量:start,finish和end_of_storage 其中&#xff1a; [start]指向容器中的起始位置 [finish]指向容器中最后一个有效数据的下一个位置 [end_of_storage]指向容器中现有容量的位置 通过这三个指针&#xff0c;就使得vector的size…...

静态成员static关键字

定义&#xff1a; 静态成员在C类中是一个重要的概念&#xff0c;它包括静态成员变量和静态成员函数。 静态成员变量 1定义&#xff1a;静态成员变量是类的所有对象共享的变量。与普通成员变量相比&#xff0c;无论创建了多少个类的实 例&#xff0c;静态成员变量只有一份拷贝…...

本地项目git同步到线上

将本地创建的项目同步到你的 GitHub 账号线上仓库&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 在 GitHub 上创建一个新仓库 登录你的 GitHub 账号。点击右上角的加号&#xff08;&#xff09;&#xff0c;然后选择 New repository。填写仓库的名称、描述等信息。选…...

Allegro如何导入DXF结构文件并生成板框(1)?

在用Allegro进行PCB设计时,需要导入DXF结构文件,由此来生成PCB的板框。 本节先讲Allegro如何导入DXF结构文件?下节讲如何利用导入的DXF结构文件生成OUTLINE板框。 Allegro如何导入DXF结构文件并生成板框(2)?-CSDN博客 详细操作方法如下: 1、选择菜单栏File 选择Import…...

Word密码忘记怎么办?三个密码找回工具帮你轻松找回密码

在工作当中&#xff0c;为了保护文档内容的安全&#xff0c;我们时常会设置密码。但有时会因为长时间未打开而忘记了密码&#xff0c;导致word文档无法打开。面对这种情况&#xff0c;我们该怎么办呢&#xff1f;下面小编就将给大家带来3个实用的密码找回工具&#xff0c;帮助大…...

使用 ABP 框架 (ASP.NET Boilerplate Project) 创建一个带有迁移功能的示例

使用 ABP 框架 (ASP.NET Boilerplate Project) 创建一个带有迁移功能的示例项目是一个很好的方式来学习如何结合高级框架进行开发。ABP 框架提供了强大的模块化和分层架构,适合构建复杂的企业级应用程序。 以下是一个使用 ABP 框架的完整示例项目,它展示了如何创建一个包含 …...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...