[Python进阶] 元类metaclass(type类)及object类

news2024/11/19 23:48:07

4.9 元类metaclass(type类)及object类

4.9.1 object类

在面向对象编程中,类是对象的蓝图或模板。类定义了一组属性和方法,并且根据该模板可以创建新的对象。由于每个对象都是基于类来创建的,因此它们共享相同的属性和方法。
object类是一个非常重要的类,它是所有类的基类。在之前的Python版本中,如果我们需要创建自己的类,需要手动继承object类。
在Python中,几乎所有的对象都可以使用它的方法和属性,包括整数、字符串、元组等内置类型的对象。例如:

x = 5
print(x.__class__) # <class 'int'>
print(x.__dir__()) # [......'bit_length', 'to_bytes', 'hex'......]

这里我们使用__class__()魔术方法打印变量所属类,调用__dir__()魔术方法打印变量的所有可用属性和方法。

4.9.2 元类metaclass(type类)

在 Python 中,元类是用来创建其他类的类。简单说,它可以看作是类的工厂,控制着类的创建过程。元类最常见的使用场景是对类进行定制化处理,比如动态添加属性或方法等。
class type是所有类的元类(metaclass)。元类是类的“类”。它们允许定义新的类,就像类定义实例对象一样。使用元类,可以控制如何创建一个类。只要派生自class type的类都是元类,并且type是默认元类。
在Python中,有2种方式定义普通类:静态定义动态生成

from icecream import ic


class A(int):  # 静态定义
    aa = 1


B = type('C', (int,), {'bb': 2})  # 动态生成

ic(type(A), type(A()))
ic(type(B), type(B()))
ic(A.aa, A().aa)
ic(B.bb, B().bb)

14:10:06|> type(A): <class ‘type’>, type(A()): <class ‘main.A’>
14:10:06|> type(B): <class ‘type’>, type(B()): <class ‘main.C’>
14:10:06|> A.aa: 1, A().aa: 1
14:10:06|> B.bb: 2, B().bb: 2

我们看到,使用class定义类其实就是通过调用type函数进行定义。两者几乎等价。

from icecream import ic


class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        return type.__new__(mcs, *args, **kwargs)


class A(metaclass=M):  # 相当于A = M('A', (), {}),而M则为A的元类
    pass

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}

如上定义class M,M是A的元类,A为M的object。而a又是A的object。在通过M创建A的时候,此时会依次运行M的__new__()方法和__init__()方法。
在一般的类中,是只会运行__new__()方法的,除非没有__new__()方法,则会运行__init__()方法。
这个时候我们可以通过重写M中的__new__()方法和__init__()方法来做一些自定义操作。
在metaclass里定义的__init__()方法,这个方法是在通过元类创建出类后调用的。

from icecream import ic
import random


class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        return type.__new__(mcs, *args, **kwargs)

    def __init__(cls, *args, **kwargs):
        print(cls, args, kwargs)
        cls.random_id = random.randint(0, 100)
        return type.__init__(cls, *args, **kwargs)


class A(metaclass=M):
    pass

ic(A.random_id)

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}
<class ‘main.A’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}
15:38:07|> A.random_id: 27

在metaclass里还可以继续定义__call__()方法,这个方法是在通过元类创建出类后,再通过该类实例化时调用的。
注意:普通的类中,在通过该类的实例化对象调用后才会运行__call__()方法。

class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        return type.__new__(mcs, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('call')
return type.__call__(cls, *args, **kwargs)


class A(metaclass=M):
    pass


a = A()

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}
call

元类一般用来解决继承没办法处理的问题。可以让你更灵活的掌握建立类中间的逻辑,比如我们可以通过class里面定义的成员来增加或删除方法。
实例一:禁止自定义类中出现‘test_’开头的方法

from icecream import ic


class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        for key in args[2]:
            if key.startswith('test_'):
                raise ValueError()
        return type.__new__(mcs, *args, **kwargs)


class A(metaclass=M):
    def test_case(self):
        pass

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’, ‘test_case’: <function A.test_case at 0x000001729106CD30>}) {}
Traceback (most recent call last):
File “E:/t3.py”, line 13, in
class A(metaclass=M):
File “E:/BaiduNetdiskWorkspace/FrbPythonFiles/Project/t3.py”, line 9, in new
raise ValueError()
ValueError

示例二:通过元类创建的类具有某些属性

from icecream import ic


class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs["author"] = "China"
        attrs["version"] = (1, 0)
        ic(cls, name, bases)
        return super().__new__(cls, name, bases, attrs)


class MyClass(metaclass=MyMeta):
    pass


print(MyClass.author)  # China
print(MyClass.version)  # (1, 0)

China
(1, 0)
16:07:04|> cls: <class ‘main.MyMeta’>, name: ‘MyClass’, bases: ()

在这个例子中,MyMeta 是一个元类,它继承自 type 类。在 MyMeta 中重写了 new 方法,在创建类时会被调用。在 new 方法中,将作者和版本号添加到传入的 attrs 参数中。
MyClass 是使用 MyMeta 元类创建的一个类,在这个类定义中,通过 metaclass 参数指定了元类。在程序执行时,MyClass 类会被创建,并且在创建时会先调用 MyMeta 的 new 方法,然后再返回一个新的类对象。最终,MyClass 对象的 author 和 version 属性会分别是 “China” 和 (1, 0)。
实例三:通过元类实现单例

class Singleton(type):
    _instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class MyClass(metaclass=Singleton):
    pass

a = MyClass()
b = MyClass()

print(a is b)  # True

这段 Python 代码实现了一个单例模式,确保在整个程序中只存在一个类的实例。
首先定义了一个 Singleton 类型,这个类实现了构造方法 call(),通过 cls._instance 变量判断当前是否已有一个实例存在,如果没有,则创建一个新的实例并将其存储在 cls._instance 变量中;如果已经有一个实例存在,则直接返回该实例。
然后我们定义另一个类 MyClass,并将它的 metaclass 设置为 Singleton,这样 MyClass 就变成了一个单例类,调用 a = MyClass() 创建的对象与 b = MyClass() 创建的对象实际上是同一个实例。
最后输出 a is b 的结果,由于 a 和 b 实际上都指向了同一个实例,因此结果为 True。

4.9.3 type类和object类的关系

元类type规定了object这个所有类的基类应该长什么样子,按照元类type的模子产生的实例object自然也就成了一个类。但是type本身也是一个类,而且object先于type,因为’help(object)‘的执行结果第一行是’class object’,object就不继承任何类了;而’help(type)‘的执行结果第一行是’class type(object)’,说明type继承object,它虽然是类的类,但‘类’这个身份也恰恰让它不得不符合类的基本形式。type、object、instance关系如下:
在这里插入图片描述

由此可见,type和object的关系可以总结为:
1、所有类都继承object(除object外)
2、所有类都是type的实例(包括type自己)

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

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

相关文章

Docker私有仓库部署与管理

目录 Docker--harbor Harbor 简介 Harbor 部署 1. 部署 Docker-Compose 服务 2. 部署 Harbor 服务 维护管理Harbor 1. 通过 Harbor Web 创建项目 2. 创建 Harbor 用户 3. 查看日志 4. 修改 Harbor.cfg 配置文件 5. 移除 Harbor 服务容器同时保留镜像数据/数据库&…

解析数字孪生的现在和未来

数字孪生是一种将现实世界与数字世界相连接的技术&#xff0c;它可以通过建立数字化的物理模型来模拟和预测现实世界的行为和性能&#xff0c;随着技术的成熟逐渐在越来越多行业得以应用&#xff0c;那有没有人好奇数字孪生是怎么来的呢&#xff1f;今天就带大家来盘一盘数字孪…

Unity中的MonoBehaviour 及其生命周期

关于MonoBehaviour 类的类图的详细介绍&#xff1a; Unity中的MonoBehaviour脚本-基础知识和继承关系_拂面清风三点水的博客-CSDN博客 关于MonoBehaviour 类的生命周期&#xff1a; Unity - Manual: Order of execution for event functions&#xff1a; Awake&#xff1a;当…

脑电信号处理与特征提取——5.频谱分析和时频分析(张治国)

目录 五、频谱分析和时频分析 5.1 频谱估计 5.1.1 基本概念 5.1.2 频谱估计方法&#xff1a;周期图 5.1.3 频谱估计方法&#xff1a;Welch法 5.1.4 频谱估计方法的比较 5.1.5 频谱特征提取 5.2 时频分析 5.2.1 短时傅里叶变换 5.2.2 连续小波变换 5.3 事件相关同步…

24考研数据结构-线性表6

目录 2.4.8 静态链表2.4.9 顺序表和链表的比较2.4.9.1 逻辑结构2.4.9.2 存储结构2.4.9.3 基本操作 - 创建2.4.9.4 基本操作 - 销毁2.4.9.5 基本操作-增/删2.4.9.6 基本操作-查2.4.9.7 顺序、链式、静态、动态四种存储方式的比较2.4.9.8 存储密度的问题2.4.9.9 存储方式的选择以…

产品需求、系统架构设计经验篇

需求设计思维导图UML 建模原型规范什么样的需求该忽略1.拍拍脑袋得来的想法&#xff0c;往往是没用的2.用户反馈的信息&#xff0c;不应该直接纳入需求3.扭改用户习惯的需求&#xff0c;一律不考虑 什么样的需求该重视1.从运维系统中根据数据结果分析得出的结论2.重视有洞见者的…

pandas pivot_table数据透视表、MultiIndex多级索引创建

参考&#xff1a; https://blog.csdn.net/ljr_123/article/details/115250639 1、 pivot_table数据透视表 import pandas as pd# 创建示例数据 data {Year: [2019, 2019, 2020, 2020, 2019, 2019, 2020, 2020],Quarter: [Q1, Q2, Q1, Q2, Q1, Q2, Q1, Q2],Product: [A, A, A…

解密数字孪生:解决实际问题的神奇技术

数字孪生是一种将现实世界与数字世界相连接的创新技术&#xff0c;通过将实际物体或系统的数据和行为模拟到数字平台上&#xff0c;实现真实与虚拟之间的交互和信息共享。数字孪生的应用不仅仅局限于虚拟现实&#xff08;VR&#xff09;和仿真领域&#xff0c;它在解决实际问题…

pytorch实现梯度下降算法例子

如题&#xff0c;利用pytorch&#xff0c;通过代码实现机器学习中的梯度下降算法&#xff0c;求解如下方程&#xff1a; f ′ ( x , y ) x 2 20 y 2 {f}(x,y) x^2 20 y^2 f′(x,y)x220y2 的最小值。 Latex语法参考&#xff1a;https://blog.csdn.net/ViatorSun/article/d…

【雕爷学编程】Arduino动手做(87)---ULN2003步进电机模组2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

openAI API使用教程、openai.Completion.create() 详细解释一下

openAI提供了几种不同场景的模型,主要有text completion、code completion、chat completion、image completion,例如chat completion,则调用方式为。而且请求的token和回复的token数会被加一起计费,例如说输入了10个token,openAI回复了20个token,那么最终收费是按照30个…

微信登录账户文件、聊天信息存储转移(存储空间足够、想保留历史聊天记录、文件)

1、打开电脑版微信、点击左下角的三根横线 2、点击左侧的“设置” 3、弹出层左侧点击“文件管理” 4、记录历史存储路径验证使用 历史默认存储位置“./我的文档/WeChat Files” 5、在其他存储空间充足盘符创建存储路径&#xff08;也可在 步骤7过程创建&#xff09; 具体位…

SpringBoot开发小技巧使用(DEBUG、启动图标修改、Lombok、devtools、Spring Initializr)

目录 1. 通过DEBUG查看自动配置的组件2. springboot启动图标修改3. Lombok4. devtools5. 通过IDEA的Spring Initializr快速创建新项目 1. 通过DEBUG查看自动配置的组件 在resources/application.properties中添加如下&#xff0c;开启DEBUG功能&#xff1a; debugtrue然后启动…

.Net Core 6.0依赖注入

.Net Core 6.0依赖注入 往期文章&#xff1a; .ner Core实现接口限流.net Core程序发布到IIS(Window Server 2019) 文章目录 .Net Core 6.0依赖注入前言一、ICO 和DI和DLICO [控制反转]DI [依赖注入]DL [依赖查找] 二、.net Core 中的依赖注入【Autofac】瞬时模式作用域模式单…

中文输入法开发-关键代码

续上篇介绍了嵌入式Linux下中文输入法&#xff0c; 嵌入式Linux下开发中文输入法_嵌入式输入法_小刚学長的博客-CSDN博客 本篇继续介绍核心关键功能 展现效果图如下&#xff1a; 1、如何跟应用关联起来&#xff0c;比如说&#xff0c;希望当LineEdit 输入状态激活后&#xff0…

怎么清空回收站?3个方法轻松搞定!

有没有大佬知道该怎么清空回收站呀&#xff1f;想把回收站清空了不知道该如何操作&#xff01;求一个清空方法&#xff01;感谢大家啦&#xff01; 电脑的回收站会为我们保存一些删除的数据&#xff0c;如果我们删除文件后意识到这些文件仍然是有用的&#xff0c;可以利用回收站…

报考CDGP一定要先有CDGA证书吗?

据DAMA中国官方网站消息&#xff0c;2023年度第三期DAMA中国CDGA和CDGP认证考试定于2023年9月23日举行&#xff0c;报名通道现已开启。 最近好多想报名CDGP认证考试的朋友都来咨询考试报名事宜&#xff0c;其中有一个重合度很高的问题“报考CDGP一定要先有CDGA证书吗&#xff…

【leetcode难题】2569. 更新数组后处理求和查询【线段树实现01翻转和区间求和模版】

题目截图 题目分析 关键就是记录每次操作2时&#xff0c;nums1中的1的个数这就需要实现线段树进行区间反转以及区间求和 ac code class Solution:def handleQuery(self, nums1: List[int], nums2: List[int], queries: List[List[int]]) -> List[int]:n len(nums1)m le…

校企合作谱新篇︱西藏农牧学院水土学院与鹏业软件签署校企合作协议

2023年7月17日&#xff0c;西藏农牧学院水利土木工程学院&#xff08;以下简称“水土学院”与成都鹏业软件股份有限公司&#xff08;以下简称“鹏业软件”&#xff09;在西藏林芝签署校企合作协议并揭牌。水土学院副书记、院长王培清、副院长孙海波、土木教研室主任宗永臣、土木…

【SpringBoot应用篇】SpringBoot+MybatisPlus集成国产DM8(达梦)数据库

【SpringBoot应用篇】SpringBootMybatisPlus集成国产DM8&#xff08;达梦&#xff09;数据库 简介和安装基本概念介绍SpringBootMP整合DM8pomymlAddressAddressMapper启动类测试类增删改查 yml配置与实体类TableName表映射问题 简介和安装 DM8 是达梦数据库有限公司推出的新一…