ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python面向对象之元类

2022-05-20 00:00:54  阅读:154  来源: 互联网

标签:__ obj name People Python self 面向对象 class 之元类


一.什么是元类

在python中一切皆对象,那么自定义的类也是一个对象,用来实例化产生该类的类称为元类\

二.为何用元类

元类是负责产生类的,那么我们自定义元类的目的就是为了控制类的产生过程,还可以控制对象的产生过程.

三.如何用元类

创建类的方法有两种

1.使用默认的元类type(type是所有自定义元类的类,如果没有继承type,那么它就是一个普通的自定义类,只有继承了type的类才是元类)

因为一切皆对象,那么用class自定义类时,就是在调用type元类实例化的一个过程

class People:#People=type(...)
def __init__(self,name,age):
self.name=name
self.age=age
那么问题来了,是怎么创建类的呢?

创建类的三要素,类名,基类们,类的名称空间

类名:顾名思义,就是一个名字,假设class_name = 'People'

基类:在python3中如果没有明显继承关系的,默认继承object类,所以class_bases = (object,...其他父类们)
类的名称空间:类的名称空间是怎么产生的?是定义类时执行类体内的代码,将产生的名字存进类的名称空间.类的名称空间可以看成是一个字典形式的存在,而代码就是一堆字符.可以定义为class_dic = {}

补充知识:我们可以通过exec方法模拟执行python代码的过程


class_bady='''
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating'%self.name)
'''

class_dic={}

exec(cmd,{},class_dic)

print(class_dic)
这样我们拿到了三要素了,可以通过tpye来创建了.

People = type(class_name,class_bases,class_dic)

分析用class来定义类的运行原理(而非元类)

1.首先拿到一个字符串格式的名字class_name = 'People'

2.再拿到一个类的基类们 class_bases = (object,)

3.执行类体内的代码,产生一个类的名称空间class_dic = {...}

4.调用People = type(class_name,class_bases,class_dic)

2.自定义元类及目的

class Mymeta(type):#只有继承了type的类才是一个元类,否则只是一个普通的自定义类
def __init__(self,class_name,class_bases,class_dic):
print(self)
print(class_name)
print(class_bases)
print(class_dic)
super().__init__(class_name,class_bases,class_dic)

class People(object,metaclass=Mymeta):#Peolpe=Mymeta('People',(object,),{...})
#metaclass就是指定一个类为元类

def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex =sex

obj = People('tom',18,'male')
2.1.自定义元类控制类的创建

class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
super().__init__(class_name, class_bases, class_dic)#重用父类的功能

dic = class_dic.get('__doc__')
if dic is None or len(dic) == 0 or len(dic.strip('\n ')) == 0:
raise TypeError('必须要有文档注释')

class People(object,metaclass=Mymeta):
'''People文档注释内容'''
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex

print(People.__dict__)
2.2.自定义元类来控制类的调用

class A:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)

obj = A()
obj(1,2,3,x=34)

#要让obj对象变成一个可调用的对象,必须再它所在的类里面定义一个__call__方法,该方法会在调用对象时自动触发

#调用obj的返回值就是__call__的返回值

通过上面的列子我们看到,调用对象就是调用对象所在类下的__call__方法.那么我们把People看成一个对象,那么调用People也就是自动触发了它所在类下必然存在的的__call__方法

class Mymeta(type):#只有继承了type的类才是一个元类,否则只是一个普通的自定义类

def __call__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
return 1

class People(object,metaclass=Mymeta):

def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex =sex

obj = People('tom',18,'male')
print(obj)

默认的,在调用obj = People('tom',18,'male')是会发生三件事

1.创建一个空对象obj

2.执行__init__方法为空对象初始化一些属性('tom',18,'male')

3.返回这个对象obj

对应的People的类中的__call__方法也应该做这三件事

class Mymeta(type): # 只有继承了type的类才是一个元类,否则只是一个普通的自定义类
def __call__(self, *args, **kwargs):#self = People
#1.创建一个空对象
obj = self.__new__(self)#obj是People这个类的对象
#这里的__new__方法是调用了object中的__new__方法,看下面的属性查找关系

#2.用__init__方法为对象初始化属性
self.__init__(obj,*args,**kwargs)#调用People下的__init__方法将接受到的参数原封不动的进行传参

#在初始化后,obj.__dict__就有了
# obj.__dict__ = {('_%s__%s'%(self.__name__,k)):v for k,v in obj.__dict__.items()}
# 基于这个逻辑我们可以施加我们想要的控制过程,比如将对象的属性私有化

#3.返回初始化好的对象obj
return obj

class People(object, metaclass=Mymeta):

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex

obj = People('tom', 18, 'male')
print(obj.__dict__)
3.属性查找

所有的属性查找其实都是先找对象再找对象所在的类

如果把类也当作对象看的话,属性查找分为两层.

第一层为对象层,先查对象(自定义的类),再查对象所在的父类,(基于c3算法MRO列表查找),一直找到object

第二层是类层(此时把类当作对象来看),如果对象层没有找到的话再查找元类层
————————————————
版权声明:本文为CSDN博主「怪丶客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42721964/article/details/82111762

标签:__,obj,name,People,Python,self,面向对象,class,之元类
来源: https://www.cnblogs.com/semwu/p/16290822.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有