元类

一、元类

1. 什么是元类

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

2. 元类有什么用

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

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

3. 内置函数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>}

4. 自定义一个元类

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)
'''        
        

5. 元类和普通类的联系

# 连着上面的自定义的元类,之后

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>}

'''       

6. 用元类控制类的创建

  • 下面的只是对自定义的普通类的名称空间做一个判断,若是要通过元类修改普通类的名称空间,就要使用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)  # 重用父类的功能

7. __call__

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

8. __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()

9. 元类控制类的实例化

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    

10. 自定义元类后的类的继承顺序

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

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

原文地址:https://www.cnblogs.com/Mcoming/p/11794150.html