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

posted @ 2019-04-25 11:44  他山之石·玉  阅读(178)  评论(0编辑  收藏  举报