Python进阶之元类

Python进阶之元类

目录
什么是元类?
元类的调用流程
根据类自定义元类
__new__方法以及参数
----------cls
----------name
----------bases
----------attrs
__call__方法
生成对象的完整代码

什么是元类?

在python面向对象中,我们知道所有的新式类都会继承object类,但是object类又是从何而来呢?是否所有的类在构建之前都会有一个框架呢?

为了方便理解我们直接上代码:

class MyClass(object):passprint(type(MyClass()))
# <class '__main__.MyClass'>
# 类的实例对象属于 MyClassprint(type(MyClass))
# <class 'type'>
# 发现类的对象属于 typeprint(type(int))
# <class 'type'>
# 发现int类也属于 typeprint(type.__base__)
# <class 'object'>
# 发现其父类是 object

根据我们已学的知识1和4不难理解,那么这个type又是什么呢?

object,class,type三者之间的关系:

  • object:object类是所有类(class)的父类,包括type类,并且object没有父类

  • class:class继承自object类,同时由type进行实例化。其中,type就是我们所要讲的元类(MetaClass)

  • type:type类是所有类的类型,即为所有类(class)都可由type实例化而来,包括object类,甚至其type本身也是由它初始化的

元类的调用流程

知道了什么是元类,那么他的调用流程也必然是我们需要知道的

在我们平时构造类时,类就是实例化对象的模板__init__初始化的则是对象的属性

那么相同的,类是由元类(MetaClass)创建的,那么元类便是类的模版

大致的调用流程图解:

image-20240108212405346

根据类自定义元类

class MyMeta(type):def __new__(cls, name, bases, attrs):# 在类创建之前执行一些操作# 可以修改类的属性和方法# 可以添加额外的属性和方法return super().__new__(cls, name, bases, attrs)class MyClass(metaclass=MyMeta):pass

在python中我们需要用到__new__()特殊方法来在对象创建之前让其经过自定义元类MyMeta(type)调用

大白话就是我们自建了一个元类,并且继承type以便调用它的特殊方法,上面的代码中我们重写了__new()__方法,接下来我们只需要在方法中对参数(cls, name, bases, attrs)进行操作即可

__new__方法以及参数

所有参数名

cls

class MyMeta(type):def __new__(cls, name, bases, attrs):print(cls) # 输出:<class '__main__.MyMeta'>print(cls.__name__) # 输出:MyMetareturn super().__new__(cls, name, bases, attrs)class My(object, metaclass=MyMeta):def __init__(self, name):self.name = namem = My('张三')

name

class MyMeta(type):def __new__(cls, name, bases, attrs):print(name) # 输出:MyClassif len(name) > 3:print("你好", end='')return super().__new__(cls, name, bases, attrs)class MyClass(object, metaclass=MyMeta):def __init__(self, name):self.name = namem = MyClass('张三')
print(m.name)
# MyClass
# 你好张三

bases

class MyMeta(type):def __new__(cls, name, bases, attrs):print(bases) # 输出:(<class '__main__.Parent'>,)return super().__new__(cls, name, bases, attrs)class Parent:passclass MyClass(Parent, metaclass=MyMeta):def __init__(self, name):self.name = namem = MyClass('张三')

attrs

class MyMeta(type):def __new__(cls, name, bases, attrs):# 修改属性attrs['modified_attr'] = 100 # 新增键modified_attrprint(attrs) # {'__module__': '__main__', '__qualname__': 'MyClass', 'original_method': <function MyClass.original_method at 0x000001B3618A1800>, 'modified_attr': 100} # 修改方法def modified_method(self):return "方法2"attrs['modified_method'] = modified_methodprint(attrs)return super().__new__(cls, name, bases, attrs)class MyClass(metaclass=MyMeta):def original_method(self):return "方法1"# 测试示例
my_obj = MyClass()
print(my_obj.modified_attr)  # 输出: 100
print(my_obj.original_method())  # 输出: 方法1
print(my_obj.modified_method())  # 输出: 方法2

__call__方法

在实例化对象的时候,会自动触发类中的__new__方法执行其中的代码,并且触发__init__方法进行初始化属性。那么此时不得不思考一个问题:为什么类会先调用__new__再调用__init__?是谁在控制这个流程?

-----__call__

调用类就是调用类的 __call__方法,类调用后的运行逻辑,其实都是 call 方法来控制的

__call__方法可以将函数或者类都转换成可调用对象,也就是我们平时调用函数时在后面加的括号:

# 函数中使用__call__
def text():print("你好")text() # 你好
text.__call__() # 你好
# 类中使用__call__
class MyClass():def __call__(self, *args, **kwargs):print('你好')my_obj = MyClass()
my_obj() # 你好

在元类中我们可以利用重写__call__方法来满足我们特定的需求:

class MyMeta(type):def __call__(cls, *args, **kwargs):print("重写__call__方法")return 123123class MyClass(metaclass=MyMeta):passmy_obj = MyClass()
print(my_obj)# 输出:123123# 用__call__方法判断类名是否为大写
class MyMeta(type):def __call__(cls, *args, **kwargs):if cls.__name__.isupper():print('is upper')else:print('is not upper')return super().__call__(*args, **kwargs)class MYCLASS(metaclass=MyMeta):passclass myclass(metaclass=MyMeta):passmy_obj = MYCLASS()  # is upper
my_obj2 = myclass()  # is not upper

__call__的执行流程:

image-20240109121001606

生成对象的完整代码

class MyMeta(type):def __new__(cls, *args, **kwargs):print("type调用了MyMeta的__new__方法,并且返回了一个对象,该对象即MyClass")return super().__new__(cls, *args, **kwargs)def __call__(cls, *args, **kwargs):print("这里调用了MyMeta的__call__方法,在这里可以对MyClass生成的对象a进行额外的操作,最终返回初始化好的对象a")instance = super().__call__(*args, **kwargs)return instancedef __init__(cls, what, bases=None, dict=None):print("当前接受的cls对象即MyClass,这里不重写直接继承并初始化数据")super(MyMeta, cls).__init__(what, bases, dict)class MyClass(metaclass=MyMeta):def __init__(self, age, name=None):self.name = name# 创建一个可调用对象
a = MyClass(18, name='张三')
t__(cls, what, bases=None, dict=None):print("当前接受的cls对象即MyClass,这里不重写直接继承并初始化数据")super(MyMeta, cls).__init__(what, bases, dict)class MyClass(metaclass=MyMeta):def __init__(self, age, name=None):self.name = name# 创建一个可调用对象
a = MyClass(18, name='张三')

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

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

相关文章

使用串口 DMA 模式接收不定长数据

一、简介 曾经遇到客户有一个需求&#xff0c;需要用串口 DMA 的方式接收不定长度的数据&#xff0c;DMA 有个缺点就是在每次传输前需要设定好传输的字节长度&#xff0c;这种方式显然对于接收不定长度的数据来说没有那么灵活。但 DMA 也有着显著的优点&#xff0c;如可直接访…

网络安全—部署CA证书服务器

文章目录 网络拓扑安装步骤安装证书系统安装从属证书服务器 申请与颁发申请证书CA颁发证书 使用windows Server 2003环境 网络拓扑 两台服务器在同一网段即可&#xff0c;即能够互相ping通。 安装步骤 安装证书系统 首先我们对计算机名进行确认&#xff0c;安装了证书系统后我…

(vue)el-cascader级联选择器实现单/多选最后一级并回显

(vue)el-cascader实现多选最后一级并回显 <el-form-item label"选择算法模型&#xff1a;"><el-cascaderv-model"formInline.algorithmId":options"modelOptions":props"{ value: id, label: name, multiple: true, emitPath: fal…

002集filter()函数及lambda()函数应用实例—python基础入门实例

1.filter()函数的基本语法如下&#xff1a; filter ( function , iterable ) 其中&#xff0c;function是一个用于判断的函数&#xff0c;iterable是一个可迭代对象&#xff0c;可以是列表、元组、集合或字符串等。filter()会将iterable中的每个元素依次传给function进行判…

python编程使用selenium模拟登陆淘宝实例代码

selenium简介 selenium 是一个web的自动化测试工具&#xff0c;不少学习功能自动化的同学开始首选selenium &#xff0c;相因为它相比QTP有诸多有点&#xff1a; * 免费&#xff0c;也不用再为破解QTP而大伤脑筋* 小巧&#xff0c;对于不同的语言它只是一个包而已&#xff0c…

【win11 绕过TPM CPU硬件限制安装】

Qt编程指南 VX&#xff1a;hao541022348 ■ 下载iso文件■ 右键文件点击装载出现如下问题■ 绕过TPM CPU硬件限制安装方法■ 虚拟机安装win11 ■ 下载iso文件 选择Windows11 &#xff08;multi-edition ISO&#xff09;在选择中文 ■ 右键文件点击装载出现如下问题 ■ 绕过T…

LINE网页版使用方法(内含LINE网页版特点总结)

如果想要在电脑上使用LINE&#xff0c;但是又觉得下载客户端很累赘的话&#xff0c;LINE网页版是你最好的选择。但是LINE网页版相对于其他平台来说使用方式比较少。所以今天就来讲讲&#xff0c;我们有什么方式可以在电脑中使用LINE。 LINE网页版使用方法 1.需要使用Chrome浏览…

作业--day42

界面设计 MyProWin::MyProWin(QWidget *parent): QMainWindow(parent) {/**********窗口主体**********///窗口大小this->setFixedSize(644, 493);this->setWindowTitle("QQ");this->setWindowIcon(QIcon("C:/Users/10988/Downloads/pictrue/pictrue/…

java编程中,保证接口幂等性的实现方案讨论

一、什么是幂等性 数学中的幂等是指f(x) f(f(x))&#xff0c;编程领域的术语是指同一个操作&#xff0c;在重复提交的情况下&#xff0c;最终产生的影响是不变的。举例说&#xff1a; 提交订单时&#xff0c;用户在购物车界面&#xff0c;重复点击“下单”&#xff0c;服务端…

摆动排序 II

题目链接 摆动排序 II 题目描述 注意点 将数组重新排列成 nums[0] < nums[1] > nums[2] < nums[3]… 的顺序题目数据保证&#xff0c;对于给定的输入 nums &#xff0c;总能产生满足题目要求的结果用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现 解答思路 如…

学习笔记16——操作系统

学习笔记系列开头惯例发布一些寻亲消息&#xff0c;感谢关注&#xff01; 链接&#xff1a;https://www.mca.gov.cn/lljz/indexdetail.html?idd0afa7f6f36946319a206d61937f9b63&type0&t10.11199120579373845 八股——操作系统一些基础知识整理 一个java程序对应一个…

FDA食品接触材料测试项目接触

1. FDA介绍&#xff1a; 美国食品和药品管理局&#xff08;FDA&#xff09;负责监管食品接触材料&#xff0c;此类材料必须经过检测&#xff0c;确保达到食品接触安全标准。美国联邦法规&#xff08;CFR&#xff09;第21章对此类材料作出具体规定&#xff0c;并将此类材料视…

MATLAB读取图片并转换为二进制数据格式

文章目录 前言一、MATLAB 文件读取方法1、文本文件读取2、二进制文件读取3、 图像文件读取4、其他文件读取 二、常用的图像处理标准图片链接三、MATLAB读取图片并转换为二进制数据格式1、matlab 源码2、运行结果 前言 本文记录使用 MATLAB 读取图片并转换为二进制数据格式的方…

Selenium 学习(0.17)——软件测试之流程图绘制方法

病假5天&#xff0c;出去野20天&#xff0c;成功错过了慕课网上的期末考试。 害&#xff0c;都怪玩乐太开心了…… 反正咱又不指着全靠这个行当来吃饭&#xff0c;错过也就错过了&#xff0c;立的Flag能抢救一下还是要抢救一下吧。【这个其实早都会画了&#xff0c;而且基本也正…

软件测试|Linux三剑客之sed命令详解

简介 sed&#xff08;Stream Editor&#xff09;是一款流式文本编辑器&#xff0c;在 Linux 和类 Unix 系统中广泛使用。它的设计目的是用于对文本进行处理和转换&#xff0c;可以用于替换、删除、插入、打印等操作。sed 命令通过逐行处理文本&#xff0c;允许您使用简单的命令…

.mkp勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 在数字时代&#xff0c;勒索病毒如[datastorecyberfear.com].mkp [hendersoncock.li].mkp [myersairmail.cc].mkp正成为企业和个人的噩梦。本文将介绍[datastorecyberfear.com].mkp [hendersoncock.li].mkp [myersairmail.cc].mkp勒索病毒的特点、如何恢复被…

HTML JavaScript 康威生命游戏

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>康威生命游戏</title><style>body {font-family: Arial, sa…

mongodb基本命令操作

1.创建数据库 语法 use 数据库名字例如:创建hero数据库 use hero查询当前数据库 db如果想查询所有的数据库 show dbs发现并没有刚刚创建的数据库,如果要显示创建的数据库,需要向表中插入一条记录 db.hero.insert({name: "zs",age: 20,country: "china&quo…

kubernetes kubeadm 集群升级(1.26.1 - 1.27.1)

kubernetes 升级 只能一个大版本大版本升级&#xff0c;也就是1.26.1-1.27.1再进行1.27.1-1.28.1 不能跳版本 升级流程 驱逐master 上的pod&#xff0c;且不可调度 kubectl drain master --ignore-daemonsets# 安装新版本的kubeadm yum install -y kubeadm-1.27.1-0 --disa…

深入理解Java源码:提升技术功底,深度掌握技术框架,快速定位线上问题

为什么要看源码&#xff1a; 1、提升技术功底&#xff1a; 学习源码里的优秀设计思想&#xff0c;比如一些疑难问题的解决思路&#xff0c;还有一些优秀的设计模式&#xff0c;整体提升自己的技术功底 2、深度掌握技术框架&#xff1a; 源码看多了&#xff0c;对于一个新技术…