• Python 元类


    一,前言

      在python的面向对象中我们可以给实例化出来的对象添加属性,这大大的提高了语言的灵活性。但是我们也知道python中的一切皆对象,我们可以通过__new__方法来控制对象的创建。那么,我们可不可以控制类的创建呢,答案是可以的,接下来我们去一探究竟。

    二,什么是元类

      元类:用来创建类的类。你创建类就是为了创建类的实例对象, 但是我们已经学习到了Python中的类也是对象 元类就是用来创建这些类(对象)的,元类就是类的类。

    三,元类详解

      上节说道,类是由元类创建的,那么谁是元类。可不可以自己定义元类。答案也是可以的。

      3.1 type()元类

      众所周知,我们可以用type()方法来查询某个对象的类型,也可以用object.__class__来查看对象的类型如下所示:

    class A(object):
        pass
    a = A()
    
    print(type('str'), 'str'.__class__.__class__)
    print(type(a), a.__class__.__class__)
    print(type(A), a.__class__.__class__)

      输出结果:

    <class 'str'> <class 'type'>
    <class '__main__.A'> <class 'type'>
    <class 'type'> <class 'type'>

      从结果中可以看出,类的类就是type,其实type就是python中的内建的元类。是它创建了类。那可不可以自己创建一个元类你,答案也是可以的

      3.2 用type来创建类

       我们知道,type可以创建类,接下来我们就用type这个元类来创建一个类。如下代码

    # 类名 = type(类名, 父类元组, 属性字典)
    A = type('A', (object, ), {'a': 1})
    a = A()
    print(a.a)

    四,__metaclass__

      4.1 __metaclass__介绍

      我们自己在创建类时可能很少用到__metaclass__这个属性,但是当你用到时Python就会用元类来创建类Foo。需要注意的是,如果仅仅是写下class A(object),其实类Foo还没有在内存中创建。Python 首先会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类A,如果没有找到,就会使用内建的type来创建这个类。当你写如下代码时 :

    class A(object):
        pass

      python要创建该类,会经过以下步骤:

    1. 查看A中是否有__metaclass__属性?如果是,Python会通过 __metaclass__创建名字为A的类(也可以理解为对象, 一切皆对象) 
    2. 如果Python没有找到__metaclass__,它会继续在其父类中寻找 __metaclass__属性,并尝试做和之前同样的操作。
    3. 如果Python在任何⽗类中都找不到__metaclass__,它就会在模块层次 中去寻找__metaclass__,并尝试做同样的操作。
    4. 如果还是找不到__metaclass__,Python就会用内置的元类type来创建这个类对象。

      从上面的步骤我们就会得出一个问题,__metaclass__怎么使用,里面可以放一些什么东西呢?

      4.2 定义元类

        在使用元类时,首先得明白,__new__返回的是一个对象,它的调用在__init__之前。

        使用元类主要的目的就是为了在创建类的时候,可以动态的改变它,提高代码的灵活性,特别是在一些API中,无法明确类的属性时非常实用。接下来我们就来试一下,如何定义一个元类:

    class A(type):
        def __new__(cls, *args, **kwargs):
            print(cls)
            print(*args)
            return type(*args, **kwargs)
    
    
    class B(object, metaclass=A):
        bar = 'ccc'
    
    
    b = B()

      输出结果:

    <class '__main__.A'>
    B (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'B', 'bar': 'ccc'}

      从上面的代码可知,在我们对对象B进行实例化时会经过如下步骤:

    1. python加载B类时,发现有metaclass属性,该属性就是A就知道,该类需要通过A来创建。就会去找A。
    2. 我们知道type创建类时,需要(类名,父类元组,类的属性字典),python就会加载这些需要的内容,并将其封装成一个元组,传给A。
    3. A接收到这些参数,就可以对其进行创建

      当然,上面的代码还有以下改写方式:

    class A(type):
        def __new__(cls, *args, **kwargs):
            print(cls)
            print(*args)
            # return type(*args, **kwargs)  # 直接用type创建
            # return type.__new__(cls, *args, **kwargs)  # 用父类的__new__方法进行创建
            return super(A, cls).__new__(cls, *args, **kwargs)  # 用继承的方式进行创建
    
    
    class B(object, metaclass=A):
        def __init__(self, a):
            self.a = a
    b = B(1)

      4.3 元类的高级用法

      下面,就是一个元类的实例,动态的类添加一个方法:

    def fun(self):  # 定义需要添加的属性
        print('fun test ....')
    
    
    class A(type):
        def __new__(cls, *args, **kwargs):
            """
            :param args: (B, (object, ), {'__init__': <B.function object>})  # 根据上面args的参数因为
            :param kwargs:{} 
            :return: 
            """
            if 'fun' not in args[2].keys():   # 若fun不存在,则添加fun函数
                args[2]['fun'] = fun 
            return type(args[0], args[1], args[2])
    
    
    class B(object, metaclass=A):
        def __init__(self, a):
            self.a = a
            
    b = B(1)
    print(hasattr(b, 'fun'))  # 判断是否有fun
    b.fun()  # 调用

    五,总结

      1,元类是用来创建类的,是祖宗类。

      2,使用元类可以增加类创建的灵活性,在创建类时动态的添加属性。

      3,python内建的元类是type

  • 相关阅读:
    Java引用类型转换
    SWFUpload多文件上传使用指南
    SpringMVC中与Spring相关的@注解
    三层——c#版
    初识三层
    vb.net 总结
    设计模式总结
    设计模式系列——装饰模式
    设计模式系列——策略模式
    设计模式系列——简单工厂模式
  • 原文地址:https://www.cnblogs.com/tashanzhishi/p/10767482.html
Copyright © 2020-2023  润新知