元类

元类

1. 什么是元类

  • 通过实例化产生类的类,称之为元类,元类实例化的结果就是class产生的类
  • 在Python中一切皆对象,那么用class关键字定义的类,本身也是对象
  • 产生该对象的类就称为元类,也可以简称为类的类

2. 为什么要有元类

  • 元类是用来产生类的,元类和自定义元类,可以控制类的产生过程和对象的产生过程

3. 创造名称空间---内置函数exec

  • exec的作用,是将字符串中的代码,提取出来执行,并产生名称空间
  • 语法exec(字符串,全局名称空间字典,局部名称空间字典)
  • 全局名称空间为{}目前不用考虑,只是占位
  • 执行字符串中的代码,产生的名字,都放到了局部名称空间字典中
  • 这一步,就模拟了类执行类体代码,产生名称空间的过程
cmd='''
x=1
y=1
print('--------------->')
'''
local_dic={}
exec(cmd,{},local_dic)

print(local_dic)
===============================
--------------->
{'x': 1, 'y': 1}
cmd='''
x=1
def func(self):
    pass
'''
class_dic={}
exec(cmd,{},class_dic)

print(class_dic)
========================================================
{'x': 1, 'func': <function func at 0x000001B4C14E1E18>}

4. 创建类的原理过程

  • 创造类的3个要素:类名父类(基类)类的名称空间
# 准备创造类的三要素:
class_name='People'    # 类名
class_bases=(object,)  # 父类(基类)
class_dic={}           # 类的名称空间
class_body='''
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age

def eat(self):
    print('%s is eating' %self.name)
'''
# 创造名称空间
exec(class_body,{},class_dic)

# 三要素:
print(class_name)
print(class_bases)
print(class_dic)
================================================================
People
(<class 'object'>,)
{'country': 'China', '__init__': <function __init__ at 0x000002446E501E18>, 'eat': <function eat at 0x000002446E6DD158>}
# 最后调用元类,即元类的实例化,产生了一个类
People=type(class_name,class_bases,class_dic)
print(People)
====================================================
<class '__main__.People'>

5. 创建类的方法一:使用class关键字创建

  • 如果说类也是对象,那么用class关键字去创建类的过程就是实例化的过程
  • 该实例化的目的是为了得到一个类,默认调用的就是元类
class People:
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def eat(self):
        print('%s is eating' %self.name)

print(type(People))
===============================================
#  默认的元类,是type,type()传入的参数是造类的关键
<class 'type'>

6. 创建类的方法二:自定义元类

  • 自定义就是为了控制类的创建过程
class Mymeta(type): # 必须继承type类,才能自定义元类,否则就是一个普通的自定义类
    # 调用元类实例化类
    def __init__(self,class_name,class_base,class_dic): # 初始化类
        print(self)    # self是类名People
        print(class_name)
        print(class_base)
        print(class_dic)
        # 最好写上,用来重用父类type元类的其他功能
        super(Mymeta, self).__init__(class_name,class_base,class_dic)

# class就是结合3个元素的3件事
class People(object,metaclass=Mymeta):  # 基类 + metaclass指定元类名
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def eat(self):
        print('%s is eating' %self.name)
====================================================================
<class '__main__.People'>
People
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x000001E56A1FD1E0>, 'eat': <function People.eat at 0x000001E56A1FD268>}

7. 自定义元类的应用

  • 自定义元类,控制类的创建过程,类的产生过程其实就是元类的调用过程

① 要求创建的类必须加文档注释

  • 控制文档注释的是__doc__

Alt text

class Mymeta(type): 
    def __init__(self,class_name,class_base,class_dic):
        # 定制创建的类中必须要有文档注释
        if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
            raise  TypeError('类中必须有文档注释,并且注释不能为空')
        super(Mymeta, self).__init__(class_name,class_base,class_dic)

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)
=======================================================================
TypeError: 类中必须有文档注释,并且注释不能为空

② 要求创建的类名开头必须大写

class Mymeta(type):
    def __init__(self,class_name,class_base,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_base,class_dic)

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)
=======================================================================
TypeError: 首字母必须大写

8. __call__

  • 将对象变为可调用的对象,就需要在该对象的类中定义__call__方法,该方法会在调用对象时,自动触发
class Foo:
    pass

obj=Foo()
obj()
=======================================
# 默认对象不能调用
TypeError: 'Foo' object is not callable
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

obj=Foo()

obj(1,2,3,x=1,y=2)
=============================================
<__main__.Foo object at 0x000001C8AB52C358>
(1, 2, 3)
{'x': 1, 'y': 2}
  • 通过自定义元类,可以控制类的调用过程,即控制类的实例化过程
  • 如果想让类可以调用,就需要在自定义的元类中添加__call__
  • 即当类被调用时,就自动触发了元类中的__call__
  • 以前是需要obj=People('xut',18)的形式,现在可以直接People()调用
  • 调类和调对象就是调__call__的方法,并且可以拿到这个方法的返回值,不写默认是None
  • 如何控制__call__,自定义元类的call
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

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)

# People()
obj=People('xut',18)

print(obj)
=======================================================
<class '__main__.People'>
('xut', 18)
{}

None # 调__call__的方法默认返回None
  • 说明:调对象(这里调的是类,People('xut',18))就是调__call__的方法,__call__返回值,赋值给一个变量(给obj
  • 如果类是对象,调类就是调元类中的__call__
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)
        # 规定了返回值
        return 123

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)


obj=People('xut',18)
print(obj)
===============================================
<class '__main__.People'>
('xut', 18)
{}
123

9. __new__

  • __new__用来创造空对象
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        # 1. 用__new__创造一个People的空对象
        self.__new__(self) # 这里的self是People
        # 2. 给这个空对象,初始化独有的属性
        print(args,kwargs)

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)


obj1=People('xut',age=18)
obj2=People('xdw',age=18)
print(obj1)
print(obj2)
============================================================
('xut',) {'age': 18}
('xdw',) {'age': 18}
None
None


People('xut',age=20)
============================================================
('xut',) {'age': 20}
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        # 1. 先造出一个People的空对象
        obj=self.__new__(self)
        # 2. 为该空对象初始化独有的属性
        self.__init__(obj,*args,**kwargs)
        # 3. 返回一个初始化好的对象
        return obj

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)

obj=People('xut',age=18)
print(obj.__dict__)
print(obj.name)
obj.eat()
==============================================
{'name': 'xut', 'age': 18}
xut
xut is eating
class Mymeta(type):
    def __call__(self, *args, **kwargs):
        # 1. 先造出一个People的空对象
        obj=self.__new__(self)
        # 2. 为该空对象初始化独有的属性
        self.__init__(obj,*args,**kwargs)
        # 3. 返回一个初始化好的对象
        return obj

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)
    def __new__(cls, *args, **kwargs):
        print(cls)
        obj=super().__new__(cls)
        return obj

obj=People('xut',age=18)
print(obj.__dict__)
print(obj.name)
obj.eat()
=============================================
<class '__main__.People'>
{'name': 'xut', 'age': 18}
xut
xut is eating
原文地址:https://www.cnblogs.com/itone/p/9248903.html