Python进阶之元类

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='张三')

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI】Pytorch 系列:预训练模型使用

1. 模型下载 import re import os import glob import torch from torch.hub import download_url_to_file from torch.hub import urlparse import torchvision.models as modelsdef download_

jenkins构建git项目timeout

问题点&#xff1a; Started by user unknown or anonymous Running as SYSTEM Building in workspace /var/jenkins_home/workspace/test-one using credential f28d956-8ee1-4f20-a32b-06879b487c70 Cloning the remote Git repository Cloning repository http://git.cc.co…

TextDiffuser-2:超越DALLE-3的文本图像融合技术

概述 近年来&#xff0c;扩散模型在图像生成领域取得了显著进展&#xff0c;但在文本图像融合方面依然存在挑战。TextDiffuser-2的出现&#xff0c;标志着在这一领域的一个重要突破&#xff0c;它成功地结合了大型语言模型的能力&#xff0c;以实现更高效、多样化且美观的文本…

HarmonyOS中的@ohos.promptAction 模块中弹框

在各种APP中会根据不同的业务场景显示不同的弹框情况&#xff0c;针对这些场景API中提示了那些弹框呢&#xff1f;今天就看下&#xff1a; 首先弹框分为模态弹框和非模态弹框&#xff0c;模态弹框必须用户点击反馈后进行下一步操作&#xff0c;非模态弹框只是告知用户信息&…

arduino安装DHT11库

步骤操作如下&#xff1a; 打开Arduino IDE。 在菜单栏中选择“项目” -> “加载库” -> “管理库…”。 在库管理器的搜索框中&#xff0c;输入“DHT sensor library”。 找到“DHT sensor library by Adafruit”&#xff0c;点击“安装”。 安装完成后&#xff0c;…

FineBI实战项目一(2):案例架构说明及数据准备

1 系统架构 基于MySQL搭建数据仓库基于Kettle进行数据处理帆软FineBI基于MySQL搭建的数据仓库进行数据分析 2 数据流程图 通过Kettle将MySQL业务系统数据库中&#xff0c;将数据抽取出来&#xff0c;然后装载到MySQL数据仓库中。编写SQL脚本&#xff0c;对MySQL数据仓库中的数…

<windows>Edge浏览器侧边栏无法关闭问题的解决方法

1 问题现象 如图&#xff0c;Edge浏览器默认安装后&#xff0c;打开了侧边栏 但是&#xff0c;关闭按钮却是灰色无法点击。 2 解决方法 在该设置页面&#xff0c;右键选择检查 在右侧的代码区域&#xff0c;选中一行便会在左侧有蓝色的标记&#xff0c;能够看到对应的页面位…

使用 CSS : 伪元素:after、过渡动画transition实现过渡效果(鼠标悬浮或点击 标签时,底部边框从左到右过渡)

首先&#xff0c;给 <span> 标签添加一个父元素&#xff0c;定义属性类名&#xff1a;nav-wrapper &#xff0c;父级设置相对定位。然后&#xff0c;使用 ::after 伪元素来创建一个与底部边框相同宽度的元素&#xff0c;初始时宽度为 0&#xff0c;通过过渡动画transitio…

IBM DS5020硬盘状态Impending failure(reported by controller)

这个状态说明硬盘还没有完全坏掉&#xff0c;但是也需要注意更换新硬盘了 磁盘状态详细信息&#xff1a; 报错信息 按照恢复步骤&#xff1a; 选中该硬盘&#xff0c;手动failed&#xff0c;之后可以将该硬盘拔掉&#xff0c;重新插入新硬盘 此时&#xff0c;会有一块热备盘启…

Angular - 笔记

文章目录 语法属性绑定引用模板变量组件绑定父组件传子组件 input子组件传父组件 outputEventEmitter ViewChildViewChildren获取子组件对象列表 管道常用模块 参考文档 语法 属性绑定 Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定&#xff0c;() 进行…

x-cmd pkg | grex - 用于生成正则表达的命令行工具

目录 简介首次用户生成的正则表达式与 perl 和 rust 兼容支持 Unicode 符号友好的用户体验进一步阅读 简介 grex 是一个旨在简化创作正则表达式的复杂且繁琐任务的库和命令行程序。这个项目最初是 Devon Govett 编写的 JavaScript 工具 regexgen 的 Rust 移植。但 regexgen 在…

RT-Thread基于AT32单片机的CAN应用

1 硬件电路 2 RT-Thread驱动配置 RT-Studio中没有CAN相关的图形配置&#xff0c;需要手动修改board.h。在board.h的末尾&#xff0c;增加相关的BSP配置。 #define RT_CAN_USING_HDR #define BSP_USING_CAN13 IO配置 at32_msp.c中的IO配置是PB9和PB10&#xff0c;掌上实验室V…

松鼠目标检测数据集VOC格式400张

松鼠&#xff0c;一种小巧玲珑、活泼可爱的啮齿类动物&#xff0c;以其蓬松的大尾巴和机敏的动作而广受欢迎。 松鼠通常体型小巧&#xff0c;四肢灵活&#xff0c;尾巴蓬松。它们的耳朵大而直立&#xff0c;眼睛明亮&#xff0c;给人留下了深刻的印象。松鼠的毛色因种类而异&a…

OLED显示模块的驱动和使用方法(附资料分享)

一、引言 本文旨在分享单片机对OLED显示模块的驱动方法和驱动代码。与此同时&#xff0c;本文还会分享如何使用取模软件进行取模&#xff0c;包括汉字取模、图片取模、英文取模。文末附驱动的代码工程和模块资料链接。 二、模块概述 1、OLED显示屏模块 2、引脚定义 3、原理图 …

npm安装vue,添加淘宝镜像

如果是第一次使用命令栏可能会遇到权限问题。 解决vscode无法运行npm和node.js命令的问题-CSDN博客 安装 在vscode上面的导航栏选择terminal打开新的命令栏 另外可能会遇到网络或者其他的问题&#xff0c;可以添加淘宝镜像 npm install -g cnpm --registryhttps://registry.…

pycharm的使用技巧

1.新建文件时,自动生成代码 settings->editor->file and code templates,选择python script ${NAME} 文件名 ${DATE} 日期 2.自动补齐自定义段落 settings->editor->live templates,在右侧点击+号,添加自定义的内容 完成之后,在下方勾选python 3.修改注释的…

「实用分享」如何用Telerik UI组件创建可扩展的企业级WPF应用?

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visual Studio…

Android逆向学习(六)绕过app签名校验,通过frida,io重定向(上)

Android逆向学习&#xff08;六&#xff09;绕过app签名校验&#xff0c;通过frida&#xff0c;io重定向&#xff08;上&#xff09; 一、写在前面 这是吾爱破解正己大大教程的第五个作业&#xff0c;然后我的系统还是ubuntu&#xff0c;建议先看一下上一个博客&#xff0c;关…

强化学习求解TSP(二):Qlearning求解旅行商问题TSP(提供Python代码)

一、Qlearning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于奖励的决策问题。它是一种无模型的学习方法&#xff0c;通过与环境的交互来学习最优策略。Q-learning的核心思想是通过学习一个Q值函数来指导决策&#xff0c;该函数表示在给定状态下采取某个动作所获…

Linux【C编程】 信号以及信号的处理方式

文章目录 1.什么是信号&#xff1f;1.1信号是内容受限的一种异步通信机制1.2信号由谁发出的&#xff1f;1.3信号由谁处理&#xff0c;如何处理 2.常见的信号3.进程对信号的处理3.1用signal函数处理SIGINT信号3.2使用sigaction 函数 4.alarm 和pause函数4.1 alarm函数详解4.2 pa…