元类

元类

我们需要牢记这句话:

一切皆对象:类实际上也是一个对象

先来举个例子:

class Person:   #Person实际上也是一个对象,一定有某个类实例化得到,而这个类就是元类
    def __init__(self,name):
        self.name = name
    def score(self):
        print('您的分数为99')

p = Person('nick')
print(p.name)  

一、什么是元类

元类:实例化产生类的类

type是内置的一个元类,所有的类都是type实例化产生

那么如何找元类呢?

class Person:   #Person实际上也是一个对象,一定有某个类实例化得到,而这个类就是元类
    def __init__(self,name):
        self.name = name
    def score(self):
        print('您的分数为99')

p = Person('nick')
print(p.name)  

print(type(p))  #<class '__main__.Person'>
print(type(Person))  #<class 'type'>他们都是由type创建出来的
print(type(list))   #<class 'type'>他们都是由type创建出来的
print(type(str))   #<class 'type'>他们都是由type创建出来的
print(type(object))   #<class 'type'>他们都是由type创建出来的
print(type(dict))    #<class 'type'>他们都是由type创建出来的
print(type(type))   #<class 'type'>他们都是由type创建出来的

二、class底层原理分析

我们需要深刻理解下面这几段话

  • class 类名会把类构造出来

  • 实际上是元类type实例化产生类这个对象

  • 类实例化产生对象一定是:类名()

我们举个简单的例子:

class Person:  #Person类是由type实例化产生,传一堆参数
    pass
p = Person()   #类实例化产生对象

此时我们会有疑问???

type是怎样传一堆参数去实例化产生一个Person类的呢?

我们打开type()的源码,会发现他是这样的type(object_or_name, bases, dict)因此它需要传3个参数分别是:

object_or_name:类的名字(是个字符串) class_name = 'Peraon'

bases: 是他的所有父类、基类们 class_bases = (object,) (object,)是个元组,所以要加个逗号

dict:类的名称空间(方法和属性们)

需求:通过type直接产生一个person类,不用class关键字

class Person: 
    def __init__(self,name):
        self.name = name
    def score(self):
        print('您的分数为99')

p = Person('yjy')
print(p.name) 

-------------------------------------------------------------------------
#方式一
#这是我们要定义的Person类的属性和方法
school='young_girl'
def __init__(self,name):
    self.name = name
def score(self):
    print('您的分数为99')

#用定义Person类
Person = type('Person',(object,),{'school':'young_girl','__init__':__init__,'score': '您的分数为99'})

p = Person('yjy')  #实例化Person类产生对象p

print(Person.__bases__)  #(<class 'object'>,)
print(Person.__dict__)   #类的名称空间
print(p.name)   #yjy


#方式二
l = {}

#exec:函数执行储存在字符串或文件中的 Python 语句
#exec将''' '''中的变量放到了局部变量l中,待会直接将l放在type()中
exec('''
school='young_girl'
def __init__(self,name):
    self.name = name
def score(self):
    print('您的分数为99')
''',{},l)

Person = type('Person',(object,),l)
p = Person('yjy')   #实例化Person类产生对象p

print(Person.__bases__)   #(<class 'object'>,)
print(Person.__dict__)    #类的名称空间
p.score()  #您的分数为99

  • exec的用法

exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码

exec(object[, globals[, locals]])   #这是exec的语法
  • object:必选参数,包含一系列Python代码中的字符串,该字符串会先被解析为一组Python语句,然后再执行
  • globals:可选参数,全局作用域(字典形式)如果不指定,默认globals
  • locals:可选参数,局部作用域(字典形式)如果不指定,默认locals

三、通过自定义元类控制类的产生

控制类的产生 == 控制元类的调用过程

自定义元类;来控制类的产生。可以控制类名,可以控制类的继承父类,控制类的名称空间

  • 自定义元类必须继承type,写一个类继承type这种类都叫元类

举个例子:

class Mymeta(type):  #只有继承了type类才能称为元类,否则只是普通的函数
    print('这里可以写一些属性和方法')

#metaclass=Mymeta指定这个类生成的时候,用自己写的Mymeta这个元类
class Person(metaclass=Mymeta):    
    pass

p = Person()

练习一:写一个自定义元类,让Person类继承

class Mymeta(type):
    #def __init__(self,*args,**kwargs):  #和下面这个对应只不过他更正规
    def __init__(self,name,bases,dic):
    	#self就是Person类
        print(name)   #Person
        print(bases)  #(<class 'object'>,)
        print(dict)   #Person的名称空间
        
class Person(object,metaclass=Mymeta):
    school='oldboy'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')
p=Person('yjy')
print(p.name)   #yjy   而且Mymeta类中的三个print都会打印,就说明他继承了Mymeta

练习二:写一个自定义元类,限制类名必须以Yjy开头

#测试一
class Mymeta(type):
    def __init__(self,name,*args,**kwargs):
        if not name.startswith('Yjy'):
            raise Exception('类名没有以Yjy开头,请做更改')

class Person(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name =name

p = Person('hhh')
print(p.name)  #Exception: 类名没有以Yjy开头,请做更改
------------------------------------------------------------
#测试二
class Mymeta(type):
    def __init__(self,name,*args,**kwargs):
        if not name.startswith('Yjy'):
            raise Exception('类名没有以Yjy开头,请做更改')

class YjyPerson(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name =name

p = YjyPerson('hhh')
print(p.name)   #hhh

练习三:限制定义的类必须加注释

#测试一
class Mymeta(type):
    def __init__(self,name,*args,**kwargs):
        if not self.__dict__['__doc__']:
            raise Exception('定义的类中没有加注释,请做更改')

class YjyPerson(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name =name

p = YjyPerson('hhh')
print(p.name)
print(p.__doc__)  #Exception: 定义的类中没有加注释,请做更改
---------------------------------------------------------
#测试二
class Mymeta(type):
    def __init__(self,name,*args,**kwargs):
        if not self.__dict__['__doc__']:
            raise Exception('定义的类中没有加注释,请做更改')

class YjyPerson(object,metaclass=Mymeta):
    '''我是类里面的注释,我必须要用三引号'''
    def __init__(self,name):
        self.name =name

p = YjyPerson('hhh')
print(p.name)   #hhh
print(p.__doc__)   #我是类里面的注释,我必须要用三引号

四、通过自定义元类控制类的调用过程

控制类的调用过程 == 对象的产生

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print('我是Mymeta元类中的__call__方法')

class Person(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print('我是Person类中的__call__方法')
        
#自动触发init的执行,此时先去触发元类中的__call__方法
p = Person('yjy')  #我是Mymeta元类中的__call__方法
p()  #TypeError: 'NoneType' object is not callable

ni你需要仔细看这两段代码有什么不同

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print('我是Mymeta元类中的__call__方法')
        obj = object.__new__(self)  #对类进行实例化,并返回该实例,用一个变量obj接收
        obj.__init__(*args, **kwargs)#和__new__一起用才是真正的构造函数
        return obj   #返回obj,这个obj其实就是Person类的名称空间

class Person(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print('我是Person类中的__call__方法')

p = Person('yjy')  #我是Mymeta元类中的__call__方法
p()   #对象加括号调用Person中的__call__方法,我是Person类中的__call__方法
print(p.name)  #yjy

练习:把对象所有的属性都变成私有

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print('我是Mymeta元类中的__call__方法')
        obj = object.__new__(self)
        obj.__init__(*args, **kwargs)
        #print(obj.__dict__)   {'name': 'yjy'}
        obj.__dict__ = {'_%s__%s'%(self.__name__,k): v for k, v in obj.__dict__.items()}
        #print(obj.__dict__)    {'_Person__name': 'yjy'}
        return obj

class Person(object,metaclass=Mymeta):
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print('我是Person类中的__call__方法')

p = Person('yjy')
p()
# print(p.name)   #AttributeError: 'Person' object has no attribute 'name'
print(p._Person__name)  #yjy

五、有了元类之后的属性查找顺序

  • 类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中找--->报错
  • 对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错

原文地址:https://www.cnblogs.com/yanjiayi098-001/p/11454304.html