Python进阶之元类

news2024/12/26 22:15:11

Python进阶之元类

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

什么是元类?

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

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

class MyClass(object):
    pass

print(type(MyClass()))
# <class '__main__.MyClass'>
# 类的实例对象属于 MyClass

print(type(MyClass))
# <class 'type'>
# 发现类的对象属于 type

print(type(int))
# <class 'type'>
# 发现int类也属于 type

print(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__) # 输出:MyMeta
        return super().__new__(cls, name, bases, attrs)


class My(object, metaclass=MyMeta):
    def __init__(self, name):
        self.name = name


m = My('张三')

name

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        print(name) # 输出:MyClass
        if len(name) > 3:
            print("你好", end='')
        return super().__new__(cls, name, bases, attrs)


class MyClass(object, metaclass=MyMeta):
    def __init__(self, name):
        self.name = name


m = 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:
    pass

class MyClass(Parent, metaclass=MyMeta):
    def __init__(self, name):
        self.name = name


m = MyClass('张三')

attrs

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        # 修改属性
        attrs['modified_attr'] = 100 # 新增键modified_attr
        print(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_method
        print(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 123123


class MyClass(metaclass=MyMeta):
    pass


my_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):
    pass


class myclass(metaclass=MyMeta):
    pass


my_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 instance

    def __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.coloradmin.cn/o/1369914.html

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

相关文章

Redis:原理速成+项目实战——Redis实战5(互斥锁、逻辑过期解决缓存击穿问题)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理速成项目实战——Redis实战4&#xff08;解决Redis缓存穿透、雪崩、击穿&#xff09; &#x1f4da;订阅专…

web左侧伸缩菜单栏/导航栏

效果展示&#xff1a; 百度网盘链接下载全部资源&#xff1a; http://链接&#xff1a;https://pan.baidu.com/s/1ZnKdWxTH49JhqZ7Xd-cJIA?pwd4332 提取码&#xff1a;4332 html/JQuery代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head&g…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷②

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷2 目录 需要竞赛软件包环境以及备赛资源可私信博主&#xff01;&#xff01;&#xff01; 2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷2 模块一 …

Spring MVC自定义类型转换器!!!

使用场景 在index.jsp里面添加日期类型 <form action"account/saveAccount" method"post">账户名称&#xff1a;<input type"text" name"name"><br/>账户金额&#xff1a;<input type"text" name&quo…

TCP的这些特性你知道吗?(滑动窗口篇)

如果每次 [发送方] 需要等待接受方返回数据才能发送下一条数据&#xff0c;会导致网络通信的效率非常的低&#xff0c;于是在TCP中 引入了窗口这个概念&#xff0c;即使在往返时间较长的情况下&#xff0c;它也不会降低网络通信的效率。有了窗口&#xff0c;并不代表可以无限 接…

windows rtmp发送数据流程抓包

一、connect 返回Window Acknowledgement Size&#xff1a; 返回Set Peer Bandwidth 二、 releaseStream 三、 FCPublish 四、 createStream 五、 _checkbw 六、 FCPublish返回 七、createStream 八、 _checkbw返回 九、发送关键帧 十、发送视频帧 十一、FCUnpublish 十二、del…

【Verilog】运算符

系列文章 数值&#xff08;整数&#xff0c;实数&#xff0c;字符串&#xff09;与数据类型&#xff08;wire、reg、mem、parameter&#xff09; 系列文章算术运算符关系运算符相等关系运算符逻辑运算符按位运算符归约运算符移位运算符条件运算符连接和复制运算符 算术运算符 …

生成模型 | 2024年新年新论文:audio2photoreal[正在更新中]

本博客主要包含了20240103新出的论文From Audio to Photoreal Embodiment: Synthesizing Humans in Conversations论文解释及项目实现~ 论文题目&#xff1a;20240103_From Audio to Photoreal Embodiment: Synthesizing Humans in Conversations 论文地址&#xff1a;2401.018…

Python2048小游戏核心算法(python系列26)

前言&#xff1a;做核心算法之前我们可以玩一玩这个小游戏来了解一下规则。2048在线试玩 运行效果&#xff1a; 代码案例&#xff1a; # 2048小游戏# 1.将列表中零移动到列表的末尾 def move_zeroes():x 0for i in range(len(list_nums)):if list_nums[i] ! 0:list_nums[x],…

python 基础笔记

基本数据类型 函数 lamda 匿名函数 成员方法 类 类与对象 构造方法 魔术方法 私有成员 私有方法 继承 注解 变量注解 函数注解 Union类型 多态 参考链接&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了_哔哩哔哩_bilib…

小测一下HCL中VSR的转发性能

正文共&#xff1a;555 字 10 图&#xff0c;预估阅读时间&#xff1a;1 分钟 上次我们在HCL中导入了NFV的自定义镜像&#xff08;如何在最新版的HCL 5.10.0中导入NFV镜像&#xff1f;&#xff09;&#xff0c;但是当时没有测试转发性能&#xff0c;最近HCL又更新了V5.10.1版本…

用golang 实现给图片添加文字水印

package mainimport ("fmt""github.com/golang/freetype""image""image/draw""image/jpeg""io""os""time" )func main() {// 打开原始图片file, err : os.Open("004.jpeg")if err …

[Vulnhub靶机] DriftingBlues: 5

[Vulnhub靶机] DriftingBlues: 5靶机渗透思路及方法&#xff08;个人分享&#xff09; 靶机下载地址&#xff1a; https://download.vulnhub.com/driftingblues/driftingblues5_vh.ova 靶机地址&#xff1a;192.168.67.24 攻击机地址&#xff1a;192.168.67.3 一、信息收集 …

多线程基础入门【Linux之旅】——下篇【死锁,条件变量,生产消费者模型,信号量】

目录 一&#xff0c;死锁 1. 死锁的必要条件 2&#xff0c;避免死锁 二&#xff0c;条件变量 同步概念与竞态条件 条件变量——初始化 静态初始化 动态初始化 pthread_cond_destroy (销毁) pthread_cond_wait (等待条件满足) pthread_cond_signal (唤醒线程) ph…

@PolarDB,你的动手体验搭子,来啦

前言 PolarDB是阿里云自研的新一代云原生数据库&#xff0c;在计算存储分离架构下&#xff0c;利用了软硬件结合的优势&#xff0c;为用户提供具备极致弹性、高性能、海量存储、安全可靠的数据库服务。100%兼容MySQL和PostgreSQL生态&#xff0c;高度兼容Oracle语法。 1月17日…

Linux下进程控制

文章目录 创建进程fork创建进程fork返回值写诗拷贝fork常规用法fork失败的原因 进程终止进程正常终止查看进程退出码_exit函数exit函数exit 和 _exit 的区别return退出 进程等待进程等待的方式wait方法(系统调用)waitpid方法(系统调用) WEXITSTATUS 和 WIFEXITED阻塞等待和非阻…

vue无法获取dom

处理过程 watch监听值变化 index.js:33 [Vue warn]: Error in callback for watcher "$store.state.modelsStorageUrl": "TypeError: Cannot set properties of undefined (setting modelScene)"watch: {"$store.state.modelsStorageUrl":{ha…

24上半年教师资格证笔试报名全流程✅

&#x1f550;考试时间安排 &#xff08;一&#xff09;报名时间&#xff1a; 2024年1月12日9:30至15日16:00 &#xff08;二&#xff09;缴费时间&#xff1a; 2024年1月17日24:00 &#xff08;三&#xff09;考试时间&#xff1a; 2024年3月9日星期六 &#xff08;四&#xf…

上网行为管理到底是什么!

之前经常听过上网行为管理系统&#xff0c;那这个系统到底是什么&#xff0c;还真不是很清楚&#xff01;今天仔细查了很多资料&#xff0c;分享给大家&#xff1a; 上网行为管理是指一种帮助互联网用户控制和管理对互联网的使用&#xff0c;包括对网页访问过滤、上网隐私保护…

vue3 useAttrs的使用场景,提取共有props

1 场景 多个类似组件都需要传参data&#xff0c;用于请求接口或者处理数据&#xff0c;想让组件干净整洁&#xff0c;把参数data提出来 2 方法 选项式 可以使用mixin混入或者extends继承&#xff08;略&#xff09; 组合式 可以使用hook 无脑式踩坑&#xff08;如下代码…