Self-study Python Fish-C Note19 P62to63

news2024/9/22 13:34:15

类和对象 (part 2)

本节主要介绍 类和对象的构造函数、重写、钻石继承、Mixin及案例源码剖析(原视频P62-63)\

构造函数

之前我们在函数章节里说,函数是可以通过参数来进行个性化定制的。类在实例化的时候其实也是支持个性化定制对象的。
定义类的时候定义一个构造函数,构造函数:__init__()
我们只需要在类中定义 __init__() 方法,那么就可以在实例化对象的同时实现个性化的定制。
实例:

class C:
    def __init__(self,x,y): # 这里写上构造函数,构造函数 __init__ 第一个参数是 self(必须)。之后两个参数是实例化对象的两个参数 x,y
        self.x = x # 注意这里 等号左边的 self.x 是绑定到实例化对象里面的x属性,等号右边的x是传进来的 x 参数
        self.y = y
    def add(self):
        return self.x + self.y
    def mul(self):
        return self.x * self.y
    
c1 = C(2,3) # 即我们在实例化的时候,就顺带着把对象的两个变量给安排进去了
print(c1.add())
print(c1.mul()) 
c1.__dict__ #我们这里内省一下,可以看到传入的参数都成了c1的属性
5
6





{'x': 2, 'y': 3}
# 传入 不同参数 测试一下:
c2 = C(4,5)
print(c2.add())
print(c2.mul())
c2.__dict__
9
20





{'x': 4, 'y': 5}

重写 与 钻石继承

重写

如果我们对于父类的某个属性和方法不满意的话,完全可以写一个同名的属性或方法对其进行覆盖。这种行为我们就称之为子类对父类的重写。
实例:

# 我们重写一个类D(继承自上面的类C),但不满足其只有两个参数,我们要多写一个参数
class D(C):
    def __init__(self,x,y,z):
        C.__init__(self,x,y) # 这一句等于:
        # self.x=x
        # self.y=y
        self.z=z
    def add(self):
        return C.add(self)+self.z
    def mul(self):
        return C.mul(self)*self.z
    
d1 = D(2,3,4)
print(d1.add())
print(d1.mul())
d1.__dict__
9
24





{'x': 2, 'y': 3, 'z': 4}

这种直接通过类名访问类里边的方法的做法,称之为调用未绑定的父类方法。 这种方法比较直接但没错,有时候可能会造成钻石继承的问题!

钻石继承

实例:

class A:
    def __init__(self):
        print('A.__init__ 调用一次')
        
# we define other 2 classes, B1 and B2, and they all inherit from A
class B1(A):
    def __init__(self):
        A.__init__(self)
        print('B1.__init__ 调用一次')
        
class B2(A):
    def __init__(self):
        A.__init__(self)
        print('B2.__init__ 调用一次')

# we define class C, which inherit from B1 and B2 at the same time
class C(B1,B2):
    def __init__(self):
        B1.__init__(self) # 我么这里调用一下类C父类的 __init__
        B2.__init__(self)
        print('C.__init__ 调用一次')
        
# we see what happened
c1=C()
A.__init__ 调用一次
B1.__init__ 调用一次
A.__init__ 调用一次
B2.__init__ 调用一次
C.__init__ 调用一次

上面的示例就是一个钻石继承发生的例子,我们可以发现当我们实例化 c1=C() 的时候,A.__init__ 被重复调用了两次。
在这里插入图片描述

究其原因:类 C 是同时继承自 类B1 和 类 B2 的。类B1 和 类 B2 又是继承自 类A 的。所以当我们 类C 去调用 类B1 和 类 B2 的构造函数的时候,类A的构造函数也会被跟着调用两次(B1一次,B2一次)。
与之对应,可以调用 super() 函数 来解决钻石继承的问题。

super() 函数

super() 函数 能够在父类中搜索指定的方法,并自动绑定好 self 参数
我们修改一下上面 类B1,类B2,类C的代码:

class B1(A):
    def __init__(self):
        super().__init__() # 这里我们用 super(),不再用未绑定的父类方法。
        # 因为我们有写清继承关系 B1是继承自A,super()自动会去搜寻父类。
        # 同时 self 也不用显示的传入了, super() 会解决
        print('B1.__init__ 调用一次')
        
class B2(A):
    def __init__(self):
        super().__init__()
        print('B2.__init__ 调用一次')

class C(B1,B2):
    def __init__(self):
        super().__init__() # 这里不用去写 B1 B2 了,它自己会去遍历
        print('C.__init__ 调用一次')

# we see what happened
c1=C()
A.__init__ 调用一次
B2.__init__ 调用一次
B1.__init__ 调用一次
C.__init__ 调用一次

此时结果就不会重复的调用了,所以我们使用 super() 函数去查找父类的方法,他就会自动按照 MRO 顺序去搜索父类的相关方法,并且自动避免重复调用的问题。\

MRO 顺序

MRO (method resolution order) 顺序(方法解析顺序): 在我们之前将多继承的时候说过,如果出现同名的属性或方法,Python 会有一个明确的查找覆盖的顺序,这个顺序就是 MRO 顺序。
如果我们要查找一个类的 MRO 顺序,有两种方法:
法1:

mro() 方法
C.mro()
[__main__.C, __main__.B1, __main__.B2, __main__.A, object]

其中 object 是所有类的基类,就算我们不写,也会被隐式的继承。

B1.mro()
[__main__.B1, __main__.A, object]
__mro__ 属性
B1.__mro__
(__main__.B1, __main__.A, object)

Mixin 案例源码剖析

Mixin 这个概念就是 mix-in,是一种设计模式(设计模式就是利用编程语言已有的特性,针对面向对象开发过程中反复出现的问题,而设计出来的解决方案)。
Mixin 示例:
原来代码:

class Animal: # 我们先定义了一个 Animal 类
    def __init__(self,name,age): # Animal 类 有构造函数,构造函数需要传入 name 和 age
        self.name = name
        self.age = age
    
    def say(self): 
        print(f'my name is {self.name}, now I am {self.age} years old')

class Pig(Animal): # 定义 Pig 类 继承 Animal 类
    def special(self):
        print('I am doing xxx')

p1 = Pig('A',5)
p1.say()
p1.special()
my name is A, now I am 5 years old
I am doing xxx

我们现在想加一个功能,让 p1 有飞的功能。其实可以写一个 飞 的功能,让 Pig 去继承他,继承一个类就可以使用类里面的方法和属性。

class Flymixin: # mixin 不是必要的,这里写上只是加以区分
    def fly(self):
        print('I am flying')

# 使用多继承,插入到 Pig 类里
class Pig(Flymixin, Animal): 
    def special(self):
        print('I am doing xxx')
        
p1 = Pig('A',5)
p1.say()
p1.special()
p1.fly()
my name is A, now I am 5 years old
I am doing xxx
I am flying

这其实就是游戏外挂的思路。下面我们有一个更复杂的例子,加深我们理解这个技巧:

class Displayer:
    def display(self, message):
        print(message)
        
class LoggerMixin:
    def log(self, message, filename='logfile.txt'):
        with open(filename,'a') as f:
            f.write(message)
            
    def display(self, message):
        super().display(message)
        self.log(message)
        
class MySubClass(LoggerMixin, Displayer):
    def log(self, message):
        super().log(message,filename='subclasslog.txt')
        
subclass = MySubClass()
subclass.display('this is a test')
this is a test

运行结果除了打印了 :this is a test,还生成了一个 ‘subclasslog.txt’ 的文件里面内容:

this is a test

详解:

subclass = MySubClass()
subclass.display('this is a test')

首先,类 MySubClass 实例化一个对象 subclass。调用 display() 方法,参数 'this is a test'
那么调用的 display() 方法 是谁的?
首先不可能是自己 MySubClass 的,因为里面没有这个方法。于是我们要从父类里面查找,这里父类有两个一个是 LoggerMixin (一般我们看到起名以Mixin结尾的就是Mixin 设计模式,是为了添加某个功能后期插上去的(想象成游戏外挂));另一个父类是 Displayer
根据先左后右的原则, Python会先去类 LoggerMixin 里查找display() 方法,然后找到:

class LoggerMixin:
    ......
    
    def display(self, message):
        super().display(message)
        self.log(message)

这里面的 display() 方法带 message 参数,就是说这里会把 'this is a test' 传递。这里方法实现的第一句,super().display(message)super() 就会去父类里面查找对应的方法。但是其实这里 类LoggerMixin 没有写父类,所以默认应该是 Object。
但是 Object 作为一个总的基类,是不会有 display() 方法的。这里是因为,super() 函数是严重依赖 MRO 顺序的,我们可以看一下 MySubClass 的 MRO 顺序:

MySubClass.mro()
[__main__.MySubClass, __main__.LoggerMixin, __main__.Displayer, object]

所以我们在类 LoggerMixin 里调用 super() 函数,它会先去类 Displayer 里查找,而不是 object。所以:

class LoggerMixin:
    ......
    
    def display(self, message):
        super().display(message)
        self.log(message)

第一句 super().display(message) 就是去调用类 Displayer 里面的display() 方法。
第二句 self.log(message),这里面 log() 方法是谁的?这里取决于 self。这里 self 就是 subclass 这个对象本身(绑定)。subclass 是 类MySubClass 的实例化对象。且 类MySubClass 里面刚好有 log() 方法:

class MySubClass(LoggerMixin, Displayer):
    def log(self, message):
        super().log(message,filename='subclasslog.txt')

这里面又有 super() 函数,那么按照 MRO 原则去 LoggerMixin 里找:

class LoggerMixin:
    def log(self, message, filename='logfile.txt'):
        with open(filename,'a') as f:
            f.write(message)

这里 log() 方法就是创建一个文件,并追加保存 message 信息。

附言:
题目:Self-study Python Fish-C Note-19 P62-P63
本文为自学B站上鱼C的python课程随手做的笔记。一些概念和例子我个人为更好的理解做了些查询和补充
因本人水平有限,如有任何问题,欢迎大家批评指正!
原视频链接:https://www.bilibili.com/video/BV1c4411e77t?p=8

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

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

相关文章

Vue.js入门系列(十八):利用浏览器本地存储实现TodoList数据持久化

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119qq.com] &#x1f4f1…

基于Python的机器学习系列(17):梯度提升回归(Gradient Boosting Regression)

简介 梯度提升(Gradient Boosting)是一种强大的集成学习方法,类似于AdaBoost,但与其不同的是,梯度提升通过在每一步添加新的预测器来减少前一步预测器的残差。这种方法通过逐步改进模型,能够有效提高预测准…

欧洲游戏市场的文化和语言特征

欧洲游戏市场是一个由无数文化和语言特征塑造的丰富多样的景观。作为世界上最大、最多样化的游戏地区之一,欧洲为旨在创造与广大受众产生共鸣的内容的开发者带来了独特的挑战和机遇。 欧洲市场最引人注目的方面之一是语言的多样性。欧盟有24种官方语言和众多地区方…

开源软件指南

目录 什么是开源软件 开源软件的历史和哲学 什么是开源许可证 开源许可证的类型 ​编辑 开源合规性 版权和知识产权合规性 安全合规性 什么是开源软件政策 开源软件安全 库存和软件物料清单(SBOM) 依赖项更新 开源漏洞扫描 二进制存储库管理器 开源软件的当前趋势…

C#语言实现最小二乘法算法

最小二乘法(Least Squares Method)是一种常用的拟合方法,用于在数据点之间找到最佳的直线(或其他函数)拟合。以下是一个用C#实现简单线性回归(即一元最小二乘法)的示例代码。 1. 最小二乘法简介…

【kubernetes】金丝雀部署

概念: 金丝雀发布(又称灰度发布、灰度更新):金丝雀发布一般先发1台,或者一个小比例,例如2%的服务器,主要做流量验证用,也称为金丝雀 (Canary) 测试 (国内常称灰度测试&a…

一文读懂Linux用户密码管理

目录 前言1. 基本知识2. 命令行 前言 作为全栈人员,对于Linux的账号密码管理也要熟知 下述文章直奔主题,直接讲解知识点 1. 基本知识 修改密码、查看密码哈希值、管理密码到期时间等 用户的密码和相关信息主要存储在两个文件中: /etc/pa…

随笔九、SARADC按键程控测试

目录 1. 泰山派环境 2. 按键3分析 3. 编程测试 1. 泰山派环境 泰山派开发板上有3个按键 按键1是电源按键PWRON,实测按几下会导致开发板重启 按键2是复位按键RESET,按下立马复位重启 按键3是升级按键RECOVER,配合RESET按键可以使开发板进…

现在做项目不看能力!

有人还在执着于能力,但当你多接触一些高手之后,你会发现他们想问题想得很透彻,什么能力,其实不重要。能力是辅助的,在互联网赚钱机会是更重要的,能力不是重点,优先的是执行力。执行力才是第一生…

泛微E9开发 获取浏览按钮的显示值

获取浏览按钮的显示值 1、显示效果2、实现方法3、标准API3.1.获取浏览按钮的显示值3.1.1.格式3.1.2.参数说明3.1.3.案例 3.2. 获取选择框字段的显示值3.2.1.获取选择框字段的显示值3.2.2.格式3.2.3.参数说明3.2.4.案例 1、显示效果 2、实现方法 <script>debugger;var tb…

IO练习:用信号实现司机和售票员问题

【1】问题描述 1&#xff09;售票员捕捉SIGINT&#xff08;代表开车&#xff09;信号&#xff0c;向司机发送SIGUSR1信号&#xff0c;司机打印&#xff08;lets gogogo&#xff09; 2&#xff09;售票员捕捉SIGQUIT&#xff08;代表停车&#xff09;信号&#xff0c;向司机发送…

x-cmd pkg | lazydocker - Docker 和 docker-compose 的 Terminal UI

目录 简介快速上手功能特点竞品和相关项目进一步探索 简介 lazydocker 是由 Jesse Duffield 用 Go 语言开发的 Docker 和 docker-compose 的 Terminal UI 管理工具。它提供 docker 的终端图形界面&#xff0c;支持鼠标操作和键盘快捷键&#xff0c;能实时监控 docker 容器&…

python简介及开发环境配置总结

参考&#xff1a;Python 基础教程 | 菜鸟教程 (runoob.com) Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言。 Python 由 Guido van Rossum 于 1989 年底发明&#xff0c;第一个公开发行版发行于 1991 年。像 Perl 语言一样, Python 源代码同样遵循 GPL(GNU G…

国风高铁站可视化:传统文化与现代科技的融合

图扑国风高铁站可视化系统展示了浓厚的传统文化元素&#xff0c;结合现代科技&#xff0c;使旅客在便捷出行的同时体验独特的文化风情。高铁站可视化管理系统通过实时监控和数据分析&#xff0c;为车站运营人员提供全面、直观的管理工具&#xff0c;提升决策效率。

物联网架构之HBase

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

06.登录页面开发

1. 前言 前面已经完成了一些基础的配置&#xff0c;接下来开始进入真正的开发阶段。开发过程中会涉及到一些其它插件的集成&#xff0c;这里用到什么就记录一下使用的心得体会。 这里需要交代一下&#xff0c;项目里的文件命名规则&#xff0c;以及文件结构&#xff0c;views…

数学建模之入门篇

目录 什么是数学建模 建模、编程、写作 一、初步建模选择模型 二、进阶熟练掌握 1. 数学模型 线性规划 图与网络模型及方法 插值与拟合 灰色预测 动态规划 层次分析法 AHP 整数规划 目标规划模型 偏最小二乘回归 微分方程模型 博弈论 / 对策论 排队论模型 存…

Java笔试面试题AI答之面向对象(5)

文章目录 25. Java 包装类的实例是否可变&#xff1f;不可变类&#xff08;Immutable Classes&#xff09;特殊情况总结 26. 简述Java什么是自动装箱和自动拆箱&#xff1f;自动装箱&#xff08;Autoboxing&#xff09;自动拆箱&#xff08;Unboxing&#xff09;注意事项 27. J…

[译] APT分析报告:12.APT29利用spy软件供应商创建的IOS、Chrome漏洞

这是作者新开的一个专栏&#xff0c;主要翻译国外知名安全厂商的技术报告和安全技术&#xff0c;了解它们的前沿技术&#xff0c;学习它们威胁溯源和恶意代码分析的方法&#xff0c;希望对您有所帮助。当然&#xff0c;由于作者英语有限&#xff0c;会借助LLM进行校验和润色&am…

秒杀抢券很难吗?oneCoupon带你成为面试中的王牌

秒杀抢券很难吗&#xff1f;oneCoupon带你成为面试中的王牌 如果你在为简历上找不到亮眼的项目发愁&#xff0c;oneCoupon牛券可能就是你的救星。这个系统不仅高性能&#xff0c;还能承受十万次查询和分发请求。本文将带你走进oneCoupon牛券的世界&#xff0c;看看它是如何助力…