Python 面向对象之元类

news2024/9/20 16:36:20

Python 面向对象之元类

【一】一切皆对象

【1】元类

  • 元类(metaclass是Python中用于创建类的类
  • 在Python中,类是对象,而元类就是类的类
  • 它们控制类的创建过程,允许你定制类的行为
  • Python中内置的默认元类是type
  • 我们用class关键字定义的所有类以及内置的类都是有元类type实例化产生

【2】class机制

  • class是Python的关键字,目的用来创建类
  • 它在底层实现类的定义本质上有四个步骤
    1. 获取类名
    2. 获取基类
    3. 获取类的名称空间
    4. 调用元类type实例化产生所定义的新类
# 使用class关键字创建
class PeaShooter(object):
    owner = "戴夫"
    def __init__(self, name):
        self.name = name
    def introduce(self):
        print(f"I`m {self.name}")

print(PeaShooter.__dict__)
# {'__module__': '__main__', 'owner': '戴夫', '__init__': <function PeaShooter.__init__ at 0x000002534E353AC0>, 'introduce': <function PeaShooter.introduce at 0x000002534E639EA0>, '__dict__': <attribute '__dict__' of 'PeaShooter' objects>, '__weakref__': <attribute '__weakref__' of 'PeaShooter' objects>, '__doc__': None}
# 使用type函数创建
# 类名
class_name = "PeaShooter"
# 基类
class_bases = (object,)
# 类的名称空间
class_dict = {}
class_body = """
owner = "戴夫"
def __init__(self, name):
    self.name = name
def introduce(self):
    print(f"T`m {self.name}")    
"""
# 类的属性和方法存储在class_dict里面. 第二个参数{}控制代码的执行环境
exec(class_body, {}, class_dict)
# 使用type函数创建
PeaSHooter = type(class_name, class_bases, class_dict)

print(PeaSHooter.__dict__)
# {'owner': '戴夫', '__init__': <function __init__ at 0x0000017E39653E20>, 'introduce': <function introduce at 0x0000017E39783AC0>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'PeaShooter' objects>, '__weakref__': <attribute '__weakref__' of 'PeaShooter' objects>, '__doc__': None}

【3】如何自定义元类

  • 元类的作用是控制类的创建
    • 也就意味着我们可以定制自己的类的具体行为
  • class机制默认的元类是type(metaclass=type
    • 也就是说我们修改metaclass参数就可以自定义元类
  • 元类是一切类的基石,所以我们自定义的元类就必须继承type
    • 目的在于使用元类的大部分功能,仅定制我们需要的那部分功能
# 自定义元类
class MyMeta(type):
    pass

# 普通的类,元类默认是type
class PeaShooter(object, metaclass=type):
    pass

# 普通自定义的类,元类是Shooter
class PeaShooter(object, metaclass=Mymeta):
    pass

【二】自定义元类控制自定义类

【1】实例化对象的本质

  • 我们知道类的实例化会触发魔法方法__new____init__
    • __new__:申请空间,创建并返回一个空对象
    • __init__:接收new创建的空对象,并初始化这个空对象
  • 我们知道实例的调用会触发魔法方法__call__
    • __call__:当对象被调用时触发

【2】控制自定义类的类名、继承关系、名称空间

(1)讲解

# 类名
class_name = "PeaShooter"
# 类的基类
class_bases = (object, )
# 类的名称空间
class_dict = {}

# 使用元类创建类
PeaShooter1 = type(class_name, class_bases, class_dict)

# 使用自定义元类创建类
class MyMeta(type):
    pass
PeaShooter2 = MyMeta(class_name, class_bases, class_dict)
  • type是Python的内置元类,而使用type创建类就是通过元类type进行类的实例化过程,即元类的实例化会自动触发魔法方法__init____new__
  • 使用元类type或者自定义元类MyMeta需要传入参数类名class_name、基类class_bases、名称空间class_dict
  • 所以可以通过自定义的元类中的魔法方法__init____new__来控制类名、基类、名称空间这些参数
  • 让我们来看看代码的执行执行结果
class MyMeta(type):
    def __init__(cls, what, bases, dict):
        print("自定义元类的魔法方法init")
        print(f"参数cls:{cls}")
        print(f"参数what:{what}")
        print(f"参数bases:{bases}")
        print(f"参数dict:{dict}")
        super().__init__(what, bases, dict)
    def __new__(cls, *args, **kwargs):
        print("自定义元类的魔法方法new")
        print(f"参数cls:{cls}")
        print(f"参数args:{args}")
        print(f"参数kwargs:{kwargs}")
        return super().__new__(cls, *args, **kwargs)

# 等价于 PeaShooter = MyMeta("PeaShooter", (object, ), {})
class PeaShooter(object, metaclass=MyMeta):
    pass
# 自定义元类的魔法方法new
# 参数cls:<class '__main__.MyMeta'>
# 参数args:('PeaShooter', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'PeaShooter'})
# 参数kwargs:{}
# 自定义元类的魔法方法init
# 参数cls:<class '__main__.PeaShooter'>
# 参数what:PeaShooter
# 参数bases:(<class 'object'>,)
# 参数dict:{'__module__': '__main__', '__qualname__': 'PeaShooter'}
(2)应用
  • 要求:自定义元类来控制创建新类

    • 类名必须含有射手(Shooter)

    • 类名首字母大写

    • 如果没有基类默认为object

    • 必须要有注释文档

  • 小坑:不可以直接使用istitle()判断首字母大写,因为这个函数会要求其他字母都是小写

class MyMeta(type):
    def __init__(cls, class_name:str, class_bases, class_dict):
        if "Shooter" not in class_name:
            raise NameError("必须含有Shooter")
        elif not class_name[0].istitle():
            print(class_name, type(class_name))
            raise NameError("必须首字母大写")
        elif not cls.__doc__:
            raise ValueError("类必须有注释文档")
        if not class_bases:
            class_bases = (object, )
        super().__init__(class_name, class_bases, class_dict)
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)
class iceShoot(metaclass=MyMeta):
    pass
# NameError: 必须含有Shooter
class iceShooter(metaclass=MyMeta):
    pass
# NameError: 必须首字母大写
class IceShooter(metaclass=MyMeta):
    pass
# ValueError: 类必须有注释文档
class IceShooter(metaclass=MyMeta):
    """
    这是寒冰射手
    """
    pass
print(IceShooter.__mro__)
# (<class '__main__.IceShooter'>, <class 'object'>)

【3】控制自定义类实例化的括号内容

(1)讲解
class MyMeta(type):

    def __call__(self, *args, **kwargs):
        print("这是自定义元类的魔法方法call")
        print(f"参数self:{self}")
        print(f"参数args:{args}")
        print(f"参数kwargs:{kwargs}")
        return super().__call__(*args, **kwargs)


class A(metaclass=MyMeta):
    def __init__(self, *args, **kwargs):
        print("这是根据自定义元类创建新类的魔法方法init")
        print(f"参数self:{self}")
        print(f"参数args:{args}")
        print(f"参数kwargs:{kwargs}")

    def __call__(self, *args, **kwargs):
        print("这是根据自定义元类创建新类的魔法方法call")
        print(f"参数self:{self}")
        print(f"参数args:{args}")
        print(f"参数kwargs:{kwargs}")


a = A("bruce", age=18)
a("tom", age=16)
# 这是自定义元类的魔法方法call
# 参数self:<class '__main__.A'>
# 参数args:('bruce',)
# 参数kwargs:{'age': 18}
# 这是根据自定义元类创建新类的魔法方法init
# 参数self:<__main__.A object at 0x00000200C1787DF0>
# 参数args:('bruce',)
# 参数kwargs:{'age': 18}
# 这是根据自定义元类创建新类的魔法方法call
# 参数self:<__main__.A object at 0x00000200C1787DF0>
# 参数args:('tom',)
# 参数kwargs:{'age': 16}
  • 我们来看看这个例子:
  • a = A("bruce", age=18)
    • 自定义的类(A)是根据自定义的元类(MyMeta)创建出来的
    • 所以这里会执行自定义元类(MyMeta)的魔法方法__call__
    • 然后将参数传递给自定义类的魔法方法___init__
    • 这两部内容是一样的,并且必须要有返回值,即将生成的类返回给实例a
    • 综上所述:我们可以通过自定义元类(MyMeta)的魔法方法__call__来控制自定义类的括号内容
  • a("tom", age=16)
    • 实例a使用括号调用了自定义类中的__call__方法,这是显而易见的,不需要解释
(2)应用
  • 要求:自定义元类来控制创建新类
    • 实例化的参数必须通过关键字参数传参
    • 不能通过位置关键字传参
class MyMeta(type):
    def __call__(cls, *args, **kwargs):
        if len(args):
            raise TypeError("实例化必须通过关键字传参")
        return super().__call__(*args, **kwargs)
        
class Student(metaclass=MyMeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age
student_one = Student("bruce", 18)
# TypeError: 实例化必须通过关键字传参
student_two = Student(name="tom", age=22)
print(student_two.name, student_two.age)
# tom 22

【三】总结

请添加图片描述

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

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

相关文章

SpringMVC概述、SpringMVC 的入门

1.MVC介绍 MVC是一种设计模式&#xff0c;将软件按照模型、视图、控制器来划分&#xff1a; M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为数据承载Bean&#xff1a;专门存储业务数据…

12、DolphinScheduler

1、DolphinScheduler简介 1.1、 DolphinScheduler概述 Apache DolphinScheduler是一个分布式、易扩展的可视化DAG工作流任务调度平台。致力于解决数据处理流程中错综复杂的依赖关系&#xff0c;使调度系统在数据处理流程中开箱即用。 1.2、 DolphinScheduler核心架构 Dolph…

Unity 踩坑记录 AnyState 切换动画执行两次

AnySate 切换动画 Can Transition To Self 将这个勾选去掉&#xff01;&#xff01;&#xff01;

九州金榜如何让孩子在家庭教育中更优秀

​ 每个人在出生时就有上天恩赐的两份礼物&#xff0c;一份是血脉相连的亲情&#xff0c;一份是家庭的关爱与教育。 最早接触的人就是父母&#xff0c;最早接触的教育就是家庭教育&#xff0c;这对孩子的影响极为深远。 这种家庭教育相比较学校教育&#xff0c;不仅有言传教…

springCould中的Config-从小白开始【10 】

目录 &#x1f32d;1.spring cloud Config是什么&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️ &#x1f953;2.能干什么&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️ &am…

借用GitHub将typora图片文件快速上传CSDN

前情概要 众所周知&#xff0c;程序员大佬们喜欢用typora软件写代码笔记&#xff0c;写了很多笔记想要放到CSDN上给其他大佬分享&#xff0c;但是在往csdn上搬运的时候&#xff0c;图片总是上传出错&#xff0c;一张一张搞有很麻烦&#xff0c;咋如何搞&#xff1f; 废话不多…

conda新建、配置python3.8虚拟环境,torch-cuda1.8,torchtext0.9.0,huggingface安装transformers库

起因是我在用bert的时候&#xff0c;导包报错 Python 环境缺少 importlib.metadata 模块。importlib.metadata 是 Python 3.8 引入的模块&#xff0c;而我的环境中使用的 Python 版本为 3.7。所以我得重新配置一个python3.8的环境 准备工作 在开始菜单找到anaconda prompt(an…

唠一唠Java线程池

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天来聊聊Java线程池&#xff0c;如果没有线程池&#xff0c;每个线程都需要手动创建和销毁线程&#xff0c;那将是多么低效和耗资源啊&#xff01; 线程池的核心作用就是复用已创建的线程&#xff0c;减少…

什么是MOM,与MES系统的差异是什么

MOM基本概念介绍 由于绝大多数MES只关注生产执行的核心作用, 对维护管理、质量管理和库存管理的重视程度有限,有时甚至缺少这部分功能, 并且未提高到与生产运营相类似的复杂程度, 难以充分满足现代制造企业对其制造运营区域的业务管理需求, 进而直接影响对企业的运营管理效果。…

自带恒压恒流环路的降压型单片车充专用芯片

一、基本概述 XL2009是一款高效降压型DC-DC转换器&#xff0c;固定180KHz开关频率&#xff0c;可以提供最高2.5A输出电流能力&#xff0c;具有低纹波&#xff0c;出色的线性调整率与负载调整率特点。XL2009内置固定频率振荡器与频率补偿电路&#xff0c;简化了电路设计。 PWM …

AArch64 memory management学习(二)

提示 该博客主要为个人学习&#xff0c;通过阅读官网手册整理而来&#xff08;个人觉得阅读官网的英文文档非常有助于理解各个IP特性&#xff09;。若有不对之处请参考参考文档&#xff0c;以官网文档为准。AArch64 memory management学习一共分为两章&#xff0c;这是第二章。…

Transformer从菜鸟到新手(五)

引言 上篇文章我们在单卡上完成了完整的训练过程。 从本文开始介绍模型训练/推理上的一些优化技巧&#xff0c;本文主要介绍多卡并行训练。 下篇文章将介绍大模型推理常用的缓存技术。 多卡训练 第一个要介绍的是利用多GPU优化&#xff0c;因为在单卡上训练实在是太慢。这…

ORACLE索引失效和sql优化

全部都是在开发中碰到的真实问题&#xff0c;后续会一直更新本条帖子&#xff1a; 场景一&#xff1a; 使用了函数&#xff0c;导致索引失效 交易日期过滤数据时使用了to_date写法&#xff0c;导致日期字段查询未走索引 将控制台输出的sql 粘贴到plsql 按F5打开解释计划执行窗口…

【Android】 ConstraintLayout实操

由于最近比较悠闲&#xff0c;重新学习了constraintlayout&#xff0c;看着官网学的&#xff0c;官网网站如下&#xff1a;https://developer.android.com/training/constraint-layout?hlzh-cn#alignment 其实之前也小小的学过一波constraintlayout&#xff0c;不过因为用线性…

C#,入门教程(13)——字符(char)及字符串(string)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(12)——数组及数组使用的基础知识https://blog.csdn.net/beijinghorn/article/details/123918227 字符串的使用与操作是必需掌握得滚瓜烂熟的编程技能之一&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; C#语言实…

Copilot 插件的使用介绍:如何快速上手

GitHub Copilot 本文主要介绍如何通过脚本工具激活 GitHub Copilot 插件&#xff0c;提供安装及激活图文教程&#xff0c;大家按下面操作即可激活GitHub Copilot插件&#xff0c;免费使用Ai编码工具 一、GitHub Copilot 介绍 GitHub Copilot 是由 GitHub 和 OpenAI 共同开发的…

Namp端口扫描

在 CentOS 7 上安装 Nmap 的步骤如下&#xff1a; 打开终端&#xff1a; 作为 root 用户&#xff0c;您已经具备执行安装命令的权限。 使用 Yum 安装 Nmap&#xff1a; 在终端中&#xff0c;输入以下命令来安装 Nmap&#xff1a;sudo yum install nmap如果您已经是 root 用户&…

浅谈配电能源管理系统应用在钢铁行业

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;能源管理系统在钢铁行业的应用不仅是钢铁行业信息化、数据化、智能化发展的重要体现&#xff0c;也是钢铁行业实现智能制造和精细化管理的重要举措。有鉴于此&#xff0c;从能源管理系统在钢铁行业的应用意义…

计算机体系结构超标量及分支预测学习记录

1.CPIIdeal CPI Structural stalls Data hazard stalls Control stalls 超标量通过动态调度&#xff0c;进一步降低CPI&#xff0c;通过每个周期发射多条指令来执行 2.猜测执行就是为了在Tomasulo算法的基础上&#xff0c;进一步解决控制冲突&#xff1b; 猜测执行算法与T…

Python(32):字符串转换成列表或元组,列表转换成字典小例子

1、python 两个列表转换成字典 字符串转换成列表 列表转换成字典 column "ID,aes,sm4,sm4_a,email,phone,ssn,military,passport,intelssn,intelpassport,intelmilitary,intelganghui,inteltaitonei,credit_card_short,credit_card_long,job,sm4_cbc,sm4_a_cbc" …