Python 面向对象之元类

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 = namedef 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 = nameself.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.mzph.cn/news/623213.shtml

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

相关文章

Rust-语句和表达式

if-else Rust中if-else表达式的作用是实现条件分支。if-else表达式的构成方式为&#xff1a;以if关键字开头&#xff0c;后面跟上条件表达式&#xff0c;后续是结果语句块&#xff0c;最后是可选的else块。条件表达式的类型必须是bool。 if-else结构还可以当表达式使用 loop …

如何用LLM和自有知识库搭建智能agent?

用LangChain建立知识库&#xff0c;文末中也推荐其他方案。 项目源码&#xff1a;ChatPDF实现 LangChain Indexes使用 对加载的内容进行索引&#xff0c;在indexes中提供了一些功能&#xff1a; Document Loaders&#xff0c;加载文档Text Splitters&#xff0c;文档切分V…

开发实践5_后台管理^/ 分_页器

以下学习 朔宁夫 开发课 。&#xff08;Python&#xff09; 一 基本使用 创建超级用户 terminal // python manage.py createsuperuser 访问地址 //Log in | Django site adminhttp://127.0.0.1:8000/admin/login/?next/admin/ superuserr login django自带admin功能。其…

一文读懂「Prompt Engineering」提示词工程

在了解提示过程之前&#xff0c;先了解一下什么是提示prompt&#xff0c;见最后附录部分 一、什么是Prompt Engingering&#xff1f; 提示工程&#xff08;Prompt Engingering&#xff09;&#xff0c;也被称为上下文提示&#xff08;In-Context Prompting&#xff09;&#x…

Django项目中的默认文件都有什么用

manager.py&#xff1a; 是django用于管理本项目的命令行工具&#xff0c;之后进行站点运行&#xff0c;数据库自动生成等都是通过本文件完成。 djangoStudy/__init__.py&#xff1a; 告诉python该目录是一个python包&#xff0c;暂无内容&#xff0c;后期一些工具的初始化可…

【数据库】间隙锁Gap Lock

什么是间隙锁 间隙锁&#xff08;Gap Lock&#xff09;&#xff1a;间隙锁是&#xff08;RR级别下&#xff09;一个在索引记录之间的间隙上的锁&#xff0c;可以是两个索引记录之间&#xff0c;也可能是第一个索引记录之前或最后一个索引之后的空间。间隙锁&#xff08;Gap Lo…

Github登录时,要二次验证.Github 2FA登录问题

2023下半年, Github登录时,要二次验证. 点击“Enable 2FA now”跳到2FA验证界面。 点setup key是到secret串。 这里有一个可以不用app的验证方法。填入secret串,运行后,会得到验证码. https://blog.csdn.net/dejavu_980323/article/details/132318107 按照这个文章,得到验证…

分布式任务调度平台XXL-JOB使用(二)

说明&#xff1a;之前总结过在CentOS系统里使用XXL-JOB。但在代码开发阶段&#xff0c;我们有时需要在本地环境测试定时任务代码&#xff0c;本文介绍如何在Windows系统使用XXL-JOB。 下载 &#xff08;1&#xff09;下载代码&#xff0c;解压打开 首先&#xff0c;去Github…

在Android原生项目中 创建 Flutter模块

前言 应用场景&#xff1a;在已有的Android原生项目中&#xff0c;引入Flutter模块&#xff0c;摸索了两天&#xff0c;终于给整出来了&#xff1b; 如果是新项目&#xff0c;最好直接创建Flutter项目&#xff0c;然后在Fluter的 android / ios目录中&#xff0c;写原生代码&…

深思熟虑可能性模型介绍与使用

深思熟虑可能性模型介绍与使用 如何联系我 作者&#xff1a;鲁伟林 邮箱&#xff1a;thinking_fioa163.com或vlinyes163.com 版权声明&#xff1a;文章和记录为个人所有&#xff0c;如果转载或个人学习&#xff0c;需注明出处&#xff0c;不得用于商业盈利行为。 背景 20…

[牛客周赛复盘] 牛客周赛 Round 28 20240114

[牛客周赛复盘] 牛客周赛 Round 28 20240114 总结A\B1. 题目描述2. 思路分析3. 代码实现 小红的炸砖块1. 题目描述2. 思路分析3. 代码实现 小红统计区间&#xff08;easy&#xff09;1. 题目描述2. 思路分析3. 代码实现 小红的好数组1. 题目描述2. 思路分析3. 代码实现 小红统…

PPT插件-大珩助手-保留原素材的位置和大小一键替换

保留原素材的位置和大小一键替换 若勾选了一键替换&#xff0c;对于从素材库插入的图形&#xff0c;可以使得它的位置、大小与幻灯片中选中的形状一致 软件介绍 PPT大珩助手是一款全新设计的Office PPT插件&#xff0c;它是一款功能强大且实用的PPT辅助工具&#xff0c;支持W…

在众多的材质中选择灰口铸铁铸造划线平台、铸铁平台等的原因——河北北重

使用灰口铸铁制作铸铁平台和划线平台的主要原因有以下几点&#xff1a; 强度高&#xff1a;灰口铸铁具有较高的强度和硬度&#xff0c;能够承受较大的载荷和冲击力。这使得灰口铸铁非常适合制作需要承受重压和磨损的平台和设备。 耐磨性好&#xff1a;灰口铸铁具有较高的耐磨性…

MySQL的多版本并发控制(MVCC)

MVCC MVCC&#xff0c;是Multiversion Concurrency Control的缩写&#xff0c;翻译过来是多版本并发控制&#xff0c;和数据库锁样&#xff0c;他也是一种并发控制的解决方案 我们知道&#xff0c;在数据库中&#xff0c;对数据的操作主要有2种&#xff0c;分别是读和写&…

MetaGPT学习笔记 - task1task2

章节&#xff1a;task1&task2 一.github地址&#xff1a;github.com/geekan/MetaGPT 二.MetaGPT: 多智能体框架​ 使 GPT 以软件公司的形式工作&#xff0c;协作处理更复杂的任务 MetaGPT输入一句话的老板需求&#xff0c;输出用户故事 / 竞品分析 / 需求 / 数据结构 / A…

1115: 数组最小值(数组)

题目描述 数组a有n个元素&#xff0c;请输出n个元素的最小值及其下标。若最小值有多个&#xff0c;请输出下标最小的一个。注意&#xff0c;有效下标从0开始。 输入 输入分两行&#xff0c;第一行是一个正整数n&#xff08;n<1000)&#xff0c;第二行是n个整数。 输出 …

Makefile编译原理 变量的使用

一.变量和不同的赋值方法 ->makefile中支持程序设计语言中变量的概念 ->makefile中的变量只代表文本数据&#xff08;字符串&#xff09; ->makefile中的变量名规则 - 变量名可以包含字符&#xff0c;数字&#xff0c;下划线。 - 不能包含 ":" "#&…

你好2024,OpenStreetMap 20 周岁

2004年&#xff0c;OpenStreetMap在英国诞生。2024年&#xff0c;OpenStreetMap 满 20 周岁&#xff0c;其愿景是创建一个免费的、可编辑的世界地图。当时&#xff0c;地图数据的获取往往受到限制或价格昂贵1。 经过20年的发展&#xff0c;该数据集合成为了最为全面的街道级别开…

宿舍维修管理系统:从数据库到前端的全面解析

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

[openGL]在ubuntu20.06上搭建openGL环境

就在刚刚, 我跑上了一个6小时后出结果的测试程序. 离下班还有很久, 于是我打开了接单群 , 发现了很多可以写的openGL项目. 但是!!我的电脑现在是ubuntu呀, 但是不要慌!!!接下来我一步一步教你如何完美搭建一个ubuntu上的openGL环境. 保证一个坑也不会踩! 文章目录 创建项目工作…