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

news2024/11/24 8:30:09
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 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2046017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot 的Web开发

Spring Boot 的Web开发 一、 静态资源映射规则 总结&#xff1a; 只要静态资源放在类路径下&#xff1a; called /static (or /public or /resources or /METAINF/resources 访问 &#xff1a; 当前项目根路径/ 静态资源名 二、 enjoy模板引擎 Enjoy模板引擎是一个轻量级的…

50ETF期权交易小技巧分享:期权的交易规律

今天带你了解50ETF期权交易小技巧分享&#xff1a;期权的交易规律。50ETF期权是一种以实物华夏上证50ETF基金为标的物的金融衍生品&#xff0c;它允许投资者在未来某个特定时间以特定价格买入或卖出一定数量的50ETF基金。 50ETF期权交易规律 1、短线思维&#xff0c;最长1周时…

Python数据分析:数据可视化(Matplotlib、Seaborn)

数据可视化是数据分析中不可或缺的一部分&#xff0c;通过将数据以图形的方式展示出来&#xff0c;可以更直观地理解数据的分布和趋势。在Python中&#xff0c;Matplotlib和Seaborn是两个非常流行和强大的数据可视化库。本文将详细介绍这两个库的使用方法&#xff0c;并附上一个…

SAP MIGO屏幕增强的具体实施步骤介绍(SE19:MB_MIGO_BADI) <转载>

原文链接&#xff1a;https://mp.weixin.qq.com/s/cDoKsc3nBPMa0GRTtQawHw 在SAP/ERP项目实施中经常会遇到MIGO屏幕增强的需求&#xff0c;就是要在MIGO标准屏幕的抬头或行项目中添加一个客制化页签&#xff0c;在增强页签下增加一些字段供用户录入一些额外信息&#xff0c;比…

C语言 | Leetcode C语言题解之第338题比特位计数

题目&#xff1a; 题解&#xff1a; int* countBits(int n, int* returnSize) {int* bits malloc(sizeof(int) * (n 1));*returnSize n 1;bits[0] 0;for (int i 1; i < n; i) {bits[i] bits[i & (i - 1)] 1;}return bits; }

公钥基础设施PKI

数字证书的生成方式&#xff1a; 第一步&#xff0c;密钥生成。有两种方法&#xff0c;一是主体可以用某个软件生成的公钥/私钥对&#xff0c;主体要使生成的私钥保密&#xff0c;然后把公钥和其他信息与身份证明发送给注册机构。二是注册机构也可以为主体生成密钥对&#xff…

前端工程师如何快速入门鸿蒙开发?

在当今科技飞速发展的时代&#xff0c;前端工程师们面临着不断拓展技能边界的挑战。随着鸿蒙操作系统的崛起&#xff0c;掌握鸿蒙开发成为了前端工程师们提升自身竞争力的重要途径。那么&#xff0c;前端工程师如何才能快速入门鸿蒙开发呢? 一、了解鸿蒙开发的基础知识 前端工…

mybatis xml 动态sql相关语法

<?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace"com.xiaKangan.mapper.EmpMapper&qu…

定期修改密码竟然导致好几任运维小哥离职

某云服务商为政企客户提供私有云服务&#xff0c;每朵云里交换机、服务器、防火墙等网络/安全设备可多达数百台&#xff0c;而该企业仅有二、三十名运维人员&#xff0c;每人要负责2-3朵云的运维管理工作。当前&#xff0c;设备管理存在一些重大安全隐患&#xff0c;主要体现在…

微服务架构的未来发展趋势

文章目录 摘要引言当前发展趋势ServerlessService MeshAIOps 未来可能出现的挑战代码示例微服务架构示例 QA环节小结未来展望参考资料 摘要 微服务架构在软件开发中已经成为主流&#xff0c;但随着市场需求和技术环境的快速变化&#xff0c;微服务架构也在不断演进。本文将分析…

在线教育新突破:Codigger 视频会议的实战案例

在当今数字化时代&#xff0c;在线教育如同一颗璀璨的新星&#xff0c;迅速崛起并照亮了知识传播的新路径。然而&#xff0c;随着竞争的加剧&#xff0c;如何提升教学质量和增强互动性&#xff0c;成为了在线教育机构亟待解决的难题。这时候&#xff0c;视频会议的出现就如同一…

【python】Matplotlib+LaTeX绘图 字符加粗 || 代码合集

【start&#xff1a;240814】 文章目录 动机Matplotlib库调用报错 LaTeX 模式安装使用配置属性&#xff1a;text.latex.preamble属性简介amsmathtextcomp 非LaTeX 模式配置属性&#xff1a;mpl.rcParams[...] 字符加粗&#xff08;label&#xff09;非LaTeX模式&#xff08;关闭…

英飞凌高压侧智能电源开关:BSP762T中文资料书

描述&#xff1a; 集成垂直功率FET的高压侧电源开关&#xff0c;提供嵌入式保护和诊断功能。 特性 - 过载保护 - 电流限制 - 短路保护 - 带重启功能的热关断 - 过压保护&#xff08;包括负载转储&#xff09; - 电感负载快速消磁 - 电池反向保护&#xff0c;带外部电阻 - CMOS兼…

【手撕数据结构】Topk问题

目录 题目思路代码 题目 TOP-K问题&#xff1a;即求数据结合中前K个最⼤的元素或者最⼩的元素&#xff0c;⼀般情况下数据量都⽐较⼤。 ⽐如&#xff1a;专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 对于Top-K问题&#xff0c;能想到的最简单直接的⽅式就是排序…

智联云采 SRM2.0 runtimeLog/download 任意文件读取漏洞复现

0x01 产品简介 智联云采是一款针对企业供应链管理难题及智能化转型升级需求而设计的解决方案&#xff0c;针对企业供应链管理难题&#xff0c;及智能化转型升级需求&#xff0c;智联云采依托人工智能、物联网、大数据、云等技术&#xff0c;通过软硬件系统化方案&#xff0c;帮…

Python 中的 SHAP 简介

本文中有多篇计划文章&#xff0c;后期会补充相关链接。鉴于公众号内无法后期修改文章&#xff0c;请关注原文链接。 如何创建和解释 SHAP 图&#xff1a;瀑布图、力图、平均 SHAP 图、蜂群图和依赖图 可直接在橱窗里购买&#xff0c;或者到文末领取优惠后购买&#xff1a; SHA…

iOS替代商店AltStore PAL获得EPIC的资助 即日起取消1.5欧元的年度订阅费

知名游戏开发商 EPIC 日前宣布通过 EPIC MegaGrant 计划向 iOS 替代商店 AltStore PAL 进行捐赠&#xff0c;该商店是在欧盟市场经过苹果批准的合规的第三方应用商店。 按苹果规定第三方替代商店需要缴纳核心技术费并且没有豁免条款&#xff0c;因此该商店每被一名用户安装&am…

RIP学习笔记

1.RIP简介 Rip&#xff1a;routing information protocol&#xff0c;路由信息协议。属于动态路由协议的一种。 RIP是应用较早、使用较普遍的内部网关协议(InteriorGatewayProtocol,简称IGP)&#xff0c;适用于小型同类网络&#xff0c;是典型的距离向量(distance-vector)协议…

OKR 与 KPI 的区别

OKR出现后&#xff0c;大家经常会拿KPI与之一起比较&#xff0c;结果两级分化&#xff1a;爱KPI的一如既往KPI&#xff0c;把OKR说的一无是处。爱OKR的&#xff0c;搞起了“去KPI”化。Tita作为OKR应用软件的服务商&#xff0c;我们也必须对OKR和KPI有充分的理解。对此我认真做…

Ubuntu离线安装docker

查看操作系统版本&#xff1a; rootzyh-VMware-Virtual-Platform:~/install# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 24.04 LTS Release: 24.04 Codename: noble rootzyh-VMware-Virtual-Platform:~/install#…