元类

1|0一、元类

1|11. 什么是元类

  • 在python中,一切皆对象,用class 关键字定义的类其本身也是一个对象。负责产生该对象的类就是元类。python中,type类就是元类,且凡是继承了type类的类,也是元类。

1|22. 元类有什么用

  • 通过自定义一个元类,来控制类的产生,还可以控制这个类的实例化对象的产生。

  • class People: x = 1 # class People 帮我们完成了 People = type(类名, 类的基类 , 类的名称空间)

1|33. 内置函数exec

cmd = """ x=1 print('exec函数运行了') def func(self): pass """ class_dic = {} # 执行cmd中的代码,然后把产生的名字丢入class_dic字典中 exec(cmd, {}, class_dic) # 第一个参数是字符串python语句,第二个是全局名称空间 ,第三个是局部名称空间 # 打印结果: exec函数运行了 print(class_dic) # 打印结果: {'x': 1, 'func': <function func at 0x10a0bc048>}

1|44. 自定义一个元类

class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __init__(self, class_name, class_bases, class_dic): print('self:', self) # self现在是People print('class_name:', class_name) print('class_bases:', class_bases) print('class_dic:', class_dic) super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类type的功能 ''' 分析用class自定义类的运行原理(而非元类的的运行原理): 1. 拿到一个字符串格式的类名class_name='People' 2. 拿到一个类的基类们class_bases=(obejct,) 3. 执行类体代码,拿到一个类的名称空间class_dic={...} 4. 调用People=type(class_name,class_bases,class_dic) '''

1|55. 元类和普通类的联系

# 连着上面的自定义的元类,之后 class People(object, metaclass=Mymeta): # People=Mymeta(类名,基类们,类的名称空间) country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) ''' 打印结果: self: <class '__main__.People'> class_name: People class_bases: (<class 'object'>,) class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x10a0bcbf8>, 'eat': <function People.eat at 0x10a0bc2f0>} '''

1|66. 用元类控制类的创建

  • 下面的只是对自定义的普通类的名称空间做一个判断,若是要通过元类修改普通类的名称空间,就要使用def __new__(cls, class_name , class_bases , class_dic)__new__ 和 __init__接收的参数是一样的,对普通类的名称空间修改之后,在通过return 方法将对象返回出去 。即obj = object.__new__(cls , class_name , class_bases , class_dic) 换行 return obj
class Mymeta(type): # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __init__(self, class_name, class_bases, class_dic): if class_dic.get('__doc__') is None or len( class_dic.get('__doc__').strip()) == 0: raise TypeError('类中必须有文档注释,并且文档注释不能为空') if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类的功能

1|77. __call__

要想让上面6中的obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法:__call__方法,该方法会在调用对象时自动触发。把 __new__ 写在 __call__ 之内即可实现。

1|88. __new__

  • 我们之前说类实例化第一个调用的是__init__,但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 new 方法。

    __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。

    注意:*new*() 函数只能用于从object继承的新式类。

class A: pass class B(A): def __new__(cls): print("__new__方法被执行") return cls.__new__(cls) def __init__(self): print("__init__方法被执行") b = B()

1|99. 元类控制类的实例化

class Mymeta(type): def __call__(self, *args, **kwargs): print(self) # self是People print(args) # args = ('nick',) print(kwargs) # kwargs = {'age':18} # return 123 # 1. 先造出一个People的空对象,申请内存空间 # __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。 obj = self.__new__(self) # 虽然和下面同样是People,但是People没有,找到的__new__是父类的 # 2. 为该对空对象初始化独有的属性 self.__init__(obj, *args, **kwargs) # 3. 返回一个初始化好的对象 return obj # People = Mymeta() 此时People 只是一个对象,通过__call__ 可以让他被调用 # People()则会触发__call__ class People(object, metaclass=Mymeta): country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) # 在调用Mymeta的__call__的时候,首先会找自己(如下函数)的,自己的没有才会找父类的 # def __new__(cls, *args, **kwargs): # # print(cls) # cls是People # # cls.__new__(cls) # 错误,无限死循环,自己找自己的,会无限递归 # obj = super(People, cls).__new__(cls) # 使用父类的,则是去父类中找__new__ # return obj

1|1010. 自定义元类后的类的继承顺序

  • 查找顺序:先对象自己——》实例化对象的类 ——》这个类的父类(多父类时按父类的继承顺序 ,新式类按照广度优先)——》object——》自定义的元类——》type

  • 注意:所有类(包括type)都继承了object类。即所有类中__new__都是最终由object中的__new__完成的。


__EOF__

本文作者BigSun丶
本文链接https://www.cnblogs.com/Mcoming/p/11794150.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   BigSun丶  阅读(120)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示