小白学python------------------面向对象之元类

一.什么是元类

在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

第二层是类层(此时把类当作对象来看),如果对象层没有找到的话再查找元类层

猜你喜欢

转载自blog.csdn.net/qq_42721964/article/details/82111762