pyhton基础【24】面向对象进阶五

目录

十五.多继承的继承顺序 - mro

调用父类方式不同导致结果不同

单继承中的super

简单总结

面试题

十六.魔术方法

魔术方法概述

魔术方法概览

__getattribute__属性

__getattribute__注意事项

常用的魔术方法

__doc__

__module__和__class__

__init__

__del__

__call__

__dict__

__getitem__、__setitem__、__delitem__


十五.多继承的继承顺序 - mro
调用父类方式不同导致结果不同

单独调用父类的方法

class Parent:def __init__(self, name):print('parent的init开始被调用...')self.name = nameprint('parent的init结束被调用...')class Son1(Parent):def __init__(self, name, age):print('Son1的init开始被调用...')self.age = ageParent.__init__(self, name)print('Son1的init结束被调用...')class Son2(Parent):def __init__(self, name, gender):print('Son2的init开始被调用...')self.gender = genderParent.__init__(self, name)print('Son2的init结束被调用...')class GrandSon(Son1, Son2):def __init__(self, name, age, gender):print('GrandSon的init开始被调用...')Son1.__init__(self, name, age)  # 单独调用父类的初始化方法Son2.__init__(self, name, gender)print('GrandSon的init结束被调用...')gs = GrandSon('grandson', 12, '男')
print('-' * 30)
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)运行结果:
GrandSon的init开始被调用...
Son1的init开始被调用...
parent的init开始被调用...
parent的init结束被调用...
Son1的init结束被调用...
Son2的init开始被调用...
parent的init开始被调用...
parent的init结束被调用...
Son2的init结束被调用...
GrandSon的init结束被调用...
------------------------------

多继承中super调用被重写的父类方法

class Parent:def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数print('parent的init开始被调用...')self.name = nameprint('parent的init结束被调用...')class Son1(Parent):def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数print('Son1的init开始被调用...')self.age = agesuper().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数print('Son1的init结束被调用...')class Son2(Parent):def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数print('Son2的init开始被调用...')self.gender = gendersuper().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数print('Son2的init结束被调用...')class GrandSon(Son1, Son2):def __init__(self, name, age, gender):print('GrandSon的init开始被调用...')# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因# super(GrandSon, self).__init__(name, age, gender)super().__init__(name, age, gender)print('GrandSon的init结束被调用...')gs = GrandSon('grandson', 12, '男')
print('-' * 30)
print(GrandSon.__mro__)  # 输出继承顺序
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)运行结果:
GrandSon的init开始被调用...
Son1的init开始被调用...
Son2的init开始被调用...
parent的init开始被调用...
parent的init结束被调用...
Son2的init结束被调用...
Son1的init结束被调用...
GrandSon的init结束被调用...
------------------------------
(<class '__main__.GrandSon'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
姓名: grandson
年龄: 12
性别: 男

上述两种调用父类的方法是有区别的:

  1. 如果两个子类中都继承了父类,当在子类中通过父类名调用时,parent被执行了两次
  2. 如果两个子类中都继承了父类,当在子类中通过super调用时,parent被执行了一次
单继承中的super
class Parent:def __init__(self, name):print('parent的init开始被调用...')self.name = nameprint('parent的init结束被调用...')class Son(Parent):def __init__(self, name, age):print('Son的init开始被调用...')self.age = agesuper().__init__(name)  # 单继承不能提供全部参数print('Son的init结束被调用...')class Grandson(Son):def __init__(self, name, age, gender):print('GrandSon的init开始被调用...')self.gender = gendersuper().__init__(name, age)  # 单继承不能提供全部参数print('GrandSon的init结束被调用...')gs = Grandson('grandson', 12, '男')
print('-' * 30)
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)运行结果:
GrandSon的init开始被调用...
Son的init开始被调用...
parent的init开始被调用...
parent的init结束被调用...
Son的init结束被调用...
GrandSon的init结束被调用...
------------------------------
姓名: grandson
年龄: 12
性别: 男
简单总结
  1. super().__init__相对于类名.__init__,在单继承上用法基本无差。
  2. 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果。
  3. 多继承时,使用super方法,对父类的传参数,由于super的算法导致的原因,必须把参数全部传递,否则会报错。
  4. 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错。
  5. 多继承时,相对于使用类名.__init__()方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因。
面试题

以下代码将会输出什么?

class Parent(object):x = 1class Child1(Parent):passclass Child2(Parent):passprint(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)输出结果:
1 1 1
1 2 1
3 2 3

使你困惑或是惊奇的是关于最后一行的输出是3 2 3而不是3 2 1。为什么改变了Parent.x的值还会改变Child2.x的值,但是同时Child1.x值却没有改变?

这个答案的关键是,在Python中,类变量在内部是作为字典处理的。如果一个变量的名字没有在当前类的字典中发现,将搜索祖先类(比如父类)直到被引用的变量名被找到(如果这个被引用的变量名既没有在自己所在的类又没有在祖先类中找到,会引发一个AttributeError异常 )。

因此,在父类中设置x = 1会使得类变量x在引用该类和其任何子类中的值为1。这就是因为第一个print语句的输出是1 1 1。

随后,如果任何它的子类重写了该值(例如,我们执行语句Child1.x = 2),然后,该值仅仅在子类中被改变。这就是为什么第二个print语句的输出是1 2 1。

最后,如果该值在父类中被改变(例如,我们执行语句Parent.x = 3),这个改变会影响到任何未重写该值的子类当中的值(在这个示例中被影响的子类是Child2)。这就是为什么第三个print输出是3 2 3

十六.魔术方法
魔术方法概述

Python中的魔术方法是一组预定义的方法,它们以双下划线开头和结尾(例如__init__、__str__ 等)。它们有特别的意义,因为Python的解释器会在某些内置行为发生时自动调用它们。魔术方法允许开发者定义或修改类的默认行为。

Python3中定义的类都是新式类,无论是否写明一个类继承object,都会间接或直接继承object。

class Person(object):pass
In [2]: dir(Person)Out[2]: 
['__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']

Python2默认不继承object

# Python2中无继承父类,称之经典类。Python3中已默认继承object
class Person:pass
Python 2.7.16 (default, Mar 25 2021, 18:52:10) 
[GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.37.14)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class Person:
...     pass
... 
>>> dir(Person)
['__doc__', '__module__']
>>>
魔术方法概览

方法名称

说明

触发方式

__init__

构造初始化函数

创建实例后赋值时使用,在__new__方法执行后自动调用

__new__

创建类的实例

创建实例时被调用

__class__

实例所在的类的地址

实例.__class__

__str__

返回实例对象的字符串信息

print(类实例)时被触发。如果没有没实现,使用repr返回的结果

__repr__

返回实例对象的字符串信息

print(repr(类实例))被触发

__del__

析构(对象被删除前做清理工作)

del实例后,如果对象引用计数为零,则自动调用

__dict__

实例自定义属性

vars(实例.__dict__)

__doc__

类文档说明,子类不继承

help(类或实例)

__getattribute__

属性访问拦截器

访问实例属性时

__bases__

获取类所有的直接父类:不能获取父类的父类

类名.__bases__

__getattribute__属性

__getattribute__功能很强大:能够完成属性访问时进行拦截

class Student:country = '中国'def __init__(self, name, age, address):self.name = nameself.age = ageself.__address = addressdef info(self):print(self.name, self.age, self.__address, Student.country)  # 使用类对象获取国家def __info_method(self):print(self.name, self.age, self.__address, self.country)  # 使用实例对象获取国家def __getattribute__(self, item):  # item传入的是属性名而不是属性值print('开始校验属性或方法:', item)return object.__getattribute__(self, item)  # 返回属性值stu = Student('安娜', 18, '长沙')print('实例对象打印属性: ', stu.name, stu.age, stu.country)  # 获取属性会触发__getattribute__
print('-' * 30)stu.info()  # 调用实例方法
print('-' * 30)stu._Student__info_method()  # 调用私有方法
print('-' * 30)
print('类对象打印属性:', Student.country)  # 使用类对象获取类属性不会触发__getattribute__'''
1.直接使用类名.类属性的形式调用类属性不会触发__getattribute__方法
2.如果在当前这个类中调用了方法则__getattribute__会获取当前这个方法的名称
'''运行结果:
开始校验属性或方法: name
开始校验属性或方法: age
开始校验属性或方法: country
实例对象打印属性:  安娜 18 中国
------------------------------
开始校验属性或方法: info
开始校验属性或方法: name
开始校验属性或方法: age
开始校验属性或方法: _Student__address
安娜 18 长沙 中国
------------------------------
开始校验属性或方法: _Student__info_method
开始校验属性或方法: name
开始校验属性或方法: age
开始校验属性或方法: _Student__address
开始校验属性或方法: country
安娜 18 长沙 中国
------------------------------
类对象打印属性: 中国
__getattribute__注意事项
class Person:def __getattribute__(self, item):print("查询指定的属性或方法是否存在...")if item.startswith("h"):return "world"else:return self.set_attribute_method()  # 会出现递归调用# try:#     return super().__getattribute__(item)# except AttributeError:#     self.set_attribute_method()@staticmethoddef set_attribute_method():return "模拟设置属性的方法..."p = Person()# 返回world
print(p.hello)# 会让程序死掉
# print(p.python)"""
不要在__getattribute__方法中使用self关键字只要self关键字使用类中的属性和方法就会触发__getattribute__
"""
常用的魔术方法
__doc__

表示类的描述信息

class Foo:""" 描述类信息,这是一个类的简单描述 """def func(self):passprint(Foo.__doc__)
__module__和__class__
  1. __module__表示当前操作的对象在哪个模块
  2. __class__表示当前操作的对象的类是谁
test.py文件class Person:def __init__(self):self.name = '安娜'
from test import Personobj = Person()
print(obj.__module__)  # 输出 test 即:输出模块
print(obj.__class__)  # 输出 test.Person 即:输出类
__init__

构造方法:通过类创建对象时,自动触发执行

class Person:def __init__(self, name):self.name = nameself.age = 18obj = Person('安娜')  # 自动执行类中的 __init__ 方法
__del__

当对象在内存中被释放之前,自动触发执行

注:此方法一般无须定义。因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以__del__的调用是由解释器在进行垃圾回收前自动触发,执行一些完善工作。

class Foo:def __del__(self):pass
__call__

对象后面加括号,触发执行

注:__init__方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于__call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()()。

class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):print('__call__')obj = Foo()  # 执行 __init__
obj()  # 执行 __call__
__dict__

类或对象中的所有属性

类的实例属性属于对象,类中的类属性和方法等属于类。

class Province:country = 'China'def __init__(self, name, count):self.name = nameself.count = countdef func(self, *args, **kwargs):print('func')# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 20000, 'name': '山西'}

__str__

如果一个类中定义了__str__方法,那么在打印对象时,默认输出该方法的返回值

class Foo:def __str__(self):return '双双'obj = Foo()
print(obj)
# 输出:双双
__getitem__、__setitem__、__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据

class Foo:def __getitem__(self, key):print('__getitem__', key)def __setitem__(self, key, value):print('__setitem__', key, value)def __delitem__(self, key):print('__delitem__', key)obj = Foo()result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = '安娜'   # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

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

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

相关文章

如何保障MySQL客户端连接数据库安全更安全

公司员工或外协人员&#xff0c;直接使用业务账号或高权限账号连接MySQL服务器&#xff0c;如同让数据在连接时减少风险——账号密码易泄露、操作行为难追溯、安全风险陡增&#xff01;尤其是在客户端连接环节&#xff0c;如何确保每一个接入点都安全可控&#xff0c;每一次操作…

机器学习入门:线性回归详解及Scikit-learn API使用指南

一、线性回归概述线性回归是统计学和机器学习领域中最基础、最广泛应用的预测建模技术之一。自19世纪初由弗朗西斯高尔顿(Francis Galton)首次提出以来&#xff0c;线性回归已成为数据分析的核心工具&#xff0c;在经济学、社会科学、生物统计学、工程学等众多领域发挥着重要作…

高斯牛顿法求解三维变换矩阵的数学推导

目录一、问题定义二、李代数基础三、雅可比矩阵推导四、高斯牛顿迭代1. 整体雅可比矩阵2. 正规方程构建3. 参数更新4. 李代数更新五、理论优势分析一、问题定义 给定两组三维点云&#xff1a;源点云 P{pi∈R3}i1NP \{p_i \in \mathbb{R}^3\}_{i1}^NP{pi​∈R3}i1N​&#xff0…

JAVA 商城系统为什么受欢迎?ZKmall开源商城灵活定制 + 插件接入适配市场

在电商系统开发这块&#xff0c;技术选得好不好&#xff0c;直接关系到平台稳不稳定、能不能扩展、适配能力强不强。JAVA 语言因为 “跨平台性突出、安全性高、可扩展性好” 这些特点&#xff0c;成了企业级电商系统的首选技术。而 ZKmall 商城基于 JAVA 开发的商城系统&#x…

【数据结构之哈夫曼树与编码实现】

文章目录 前言一、哈夫曼树与哈夫曼编码简介1. 什么是哈夫曼树&#xff1f;2. 为什么需要哈夫曼编码&#xff1f; 二、哈夫曼编码原理三、哈夫曼树的构建步骤详解1. 统计字符频率2. 定义哈夫曼树节点3. 最小堆&#xff08;优先队列&#xff09;的构造4. 合并节点&#xff0c;构…

基于Hadoop的京东厨具商品数据分析及商品价格预测系统的设计与实现

文章目录有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍数据采集用户界面系统展示管理员界面每文一语有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 本项目围绕“京东厨具数据分析系统的设计与实…

深入解析TCP:可靠传输的核心机制与实现逻辑(三次握手、四次挥手、流量控制、滑动窗口、拥塞控制、慢启动、延时应答、面向字节流、粘包问题)

Linux系列 文章目录 Linux系列一、TCP连接的建立与断开1.1 TCP 三次握手1.2 TCP四次挥手1. TCP连接的本质是应用层间的通信通道2. 断开连接的核心是终止应用层通信3. 常见误解澄清 二、TCP协议的机制2.1 流量控制2.2 滑动窗口2.2.1 滑动窗口的工作原理2.2.2 基于滑动窗口快重传…

基于开源AI智能客服、AI智能名片与S2B2C商城小程序的微商服务质量提升路径研究

摘要&#xff1a;在科技飞速发展的背景下&#xff0c;产品技术含量与复杂度显著提升&#xff0c;客户正确使用产品并体验其价值愈发依赖代理的专业指导与服务。本文聚焦开源AI智能客服、AI智能名片与S2B2C商城小程序在微商服务中的应用&#xff0c;通过分析其技术原理与实践案例…

[netty5: HttpHeaders HttpHeadersFactory]-源码分析

HttpHeaders HttpHeaders 是用于存储和操作HTTP请求或响应头部字段的接口。 // DefaultHttpHeaders, HttpHeadersFactory.TrailingHttpHeaders public interface HttpHeaders extends Iterable<Entry<CharSequence, CharSequence>> {static HttpHeaders emptyHead…

基于Flink 1.20、StarRocks与TiCDC构建高效数据处理链路教程

在大数据处理领域&#xff0c;实现高效、实时的数据处理与分析至关重要。Flink作为强大的流批一体化计算框架&#xff0c;结合StarRocks这一高性能的实时分析型数据库&#xff0c;再搭配TiCDC&#xff08;TiDB Change Data Capture&#xff09;用于捕获数据变更&#xff0c;能够…

便捷的Office批量转PDF工具

软件介绍 本文介绍的软件是一款能实现Office批量转换的工具&#xff0c;名为五五Excel word批量转PDF。 软件小巧 这款五五Excel word批量转PDF软件大小不到2M。 操作步骤一 使用该软件时&#xff0c;只需把软件和需要转换的Word或Excel文件放在同一个文件夹里。 操作步骤…

tcp长连接与短连接

TCP连接本身是一个传输层协议&#xff0c;它既可以实现长连接&#xff0c;也可以实现短连接。这取决于应用层的使用方式。 短连接&#xff08;Short Connection&#xff09; 特点&#xff1a;每次请求都建立新的TCP连接&#xff0c;完成后立即关闭流程&#xff1a;建立连接 →…

llvm polly,亲自测试

1&#xff09;下载并安装 Polly - Getting Started git clone https://github.com/llvm/llvm-project.git 大概需要半个小时&#xff0c;有时候被墙掉就打不开 2&#xff09; mkdir build && cd build cmake -DLLVM_ENABLE_PROJECTSclang;polly ../llvm cmake --b…

Spring AI 项目实战(十四):Spring Boot + Vue3 +AI + DeepSeek 实现空气质量智能预测系统(附完整源码)

系列文章 序号文章名称1Spring AI 项目实战(一):Spring AI 核心模块入门2Spring AI 项目实战(二):Spring Boot + AI + DeepSeek 深度实战(附完整源码)3Spring AI 项目实战(三):Spring Boot + AI + DeepSeek 打造智能客服系统(附完整源码)4

腾讯云 CDN 不支持 WebSocket 的现状与华为云 CDN 的替代方案-优雅草卓伊凡

腾讯云 CDN 不支持 WebSocket 的现状与华为云 CDN 的替代方案-优雅草卓伊凡 问题背景 卓伊凡今天发现&#xff0c;腾讯云 CDN 不支持 WebSocket 协议&#xff0c;而公司的部分业务&#xff08;如实时聊天、在线协作、游戏互动、股票行情推送等&#xff09;依赖长连接通信。昨…

MybatisPlus(一)扩展功能

扩展功能 一、静态工具二、逻辑删除三、通用枚举1、定义枚举2、配置枚举处理器3、测试 四、JSON类型处理器1、定义实体2、使用类型处理器 五、分页1、配置分页插件2、分页API3、示例 一、静态工具 有的时候Service之间也会相互调用&#xff0c;为了避免出现循环依赖问题&#…

Redis哨兵模式之Sentinel模式(二)

一、多节点哨兵如何配置&#xff1f; 哨兵配置原理图 注意&#xff1a;sentinel哨兵模式的搭建是建立在redis主从复制节点配置基础而搭建&#xff0c;在主从配置中从库需要配置好replicaof关联上主库并关闭安全模式&#xff0c;然后设置好bind端口才能关联上机器&#xff0c;而…

基于Excel的数据分析思维与分析方法

数据分析一定要会Excel、SQL和Python&#xff1f;非常肯定地回答您&#xff0c;Python、R语言、Excel函数和VBA&#xff0c;以及高级数据分析软件&#xff0c;都学不到&#xff0c;您将学到&#xff1a;5个有效的数据分析利器&#xff0c;以及分析思维 一、描述性统计分析 在…

计算机网络笔记(不全)

一、计算机网络体系结构1.计算机网络的概念计算机网络&#xff1a;由若干结点和连接这些结点的链路组成。结点可以是计算机、集线器、交换机、路由器等。互连网(internet)&#xff1a;多个计算机网络通过路由器互相连接而成&#xff0c;可用任意协议通信。互联网(因特网Interne…

XML Schema 复合元素

XML Schema 复合元素 引言 XML(可扩展标记语言)作为一种灵活的标记语言,广泛应用于数据交换和存储。XML Schema 是一种用于描述和定义 XML 文档结构的语言,它定义了 XML 文档的元素、属性、类型和约束。本文将详细介绍 XML Schema 中的复合元素,并探讨其在实际应用中的重…