Python MetaClass

在 Python 中,我们经常使用 type 来查看对象的类型,如下:

def test01():
    number = 100
    print(type(number))

if __name__ == '__main__':
    test03()

type 除此用法之外,还可以动态创建类。所谓动态创建类指的是指在运行时通过编程方式创建新的类,而不是在源代码中静态定义类。

1. type 类的动态构建

我们创建的静态类如下:

class ClassName:

    class_attr = 100

    def __init__(self, number):
        self.number = number

    def instance_method(self):
        print('hello instance method', self.number)

    @classmethod
    def class_method(cls):
        print('hello class method', cls.class_attr)

    @staticmethod
    def static_method():
        print('hello static method')

如果,我们使用编程的方式动态创建这样的类,可以使用 type 来实现。首先,初始化 type 类型对象。然后通过 type 对象创建实例对象。type 类的 __init__ 方法参数如下:

# name: 字符串,类名
# bases:元祖,基类
# dict: 字典,类属性和方法
type.__init__(name, bases, dict)
def test():

    def instance_init(self, number):
        print('hello __init__ method')
        self.number = number

    ClassName = type('ClassName', (), {
        # 绑定类属性
        'class_attr': 100,
        # 绑定实例方法
        'instance_method': lambda self: print('hello instance method', self.number),
        # 绑定魔术方法
        '__init__': instance_init,
        # 绑定类方法
        'class_method': classmethod(lambda cls: print('hello class method', cls.class_attr)),
        # 绑定静态方法
        'static_method': staticmethod(lambda : print('hello static method'))
    })

    # 调用类方法
    ClassName.class_method()
    # 调用静态方法
    ClassName.static_method()

    # 调用实例方法
    object = ClassName(200)
    object.instance_method()


if __name__ == '__main__':
    test()
hello class method 100
hello static method
hello __init__ method
hello instance method 200

从这个例子可以看出,所有的 Python 类对象都是由 type 创建出来,再由各个 Python 类对象创建各种各样的实例对象。上例中:首先由 type 动态创建出 ClassName 类,然后由 ClassName 类创建 object 实例对象。

2. 自定义 type 类

我们先了解下 type 动态创建实例对象的过程:

  1. 首先,调用 type 类的 __new__ 方法创建自定义类对象;
  2. 然后,调用 type 类的 __init__ 方法对类对象进行初始化;
  3. 此时,得到自定义类对象,即:得到类型;
  4. 接着,调用 type 类的 __call__ 方法创建实例对象;
  5. 最后,调用自定义类对象的 __init__ 方法初始化实例化对象;
  6. 此时,完成动态类的实例对象的创建。

下面自定义 Meta 类并继承 type 类。当创建类对象(调用 __new__ 方法)时,我们通过 attrs 字典参数给类对象绑定相应的成员:

  1. 类属性 class_attr
  2. 实例方法 instance_function
  3. 类方法 class_function
  4. 静态方法 static_function
class Meta(type):

    def __new__(cls, cname, bases, attrs):
        # 绑定类属性
        attrs['class_attr'] = 100
        # 绑定实例方法
        attrs['instance_function'] = lambda self: print('Meta 动态绑定实例方法')
        # 绑定类方法
        attrs['class_function'] = classmethod(lambda self: print('Meta 动态绑定实例方法'))
        # 绑定静态方法
        attrs['static_function'] = staticmethod(lambda : print('Meta 动态绑定静态方法'))
        # 调用父类 type.__new__ 方法创建类对象
        class_object = super().__new__(cls, cname, bases, attrs)
        print('Meta new ', cls, class_object, cname, bases, attrs)

通过 __init__ 方法对创建好的类对象进行初始化。这个函数我们仅仅是调用父类 type 的 init 方法,我们自己并没有什么需要对类对象进行操作的行为。

class Meta(type):

    # 初始化类对象
    def __init__(cls, cname, bases, attrs):
        super().__init__(cname, bases, attrs)
        print('Meta init', cls, cname, bases, attrs)

接下来,调用调用 Meta 的 new 、init 方法构建相应的类对象。除此之外,我们也手动给两个不同的类对象分别 param1、param2 类属性,以及 init 初始化方法。

def test():
    Object1 = Meta('Object1', (), {
        'param1': 100,
        '__init__': lambda self, x, y: print('object1 init', x, y),
    })

    Object2 = Meta('Object2', (), {
        'param2': 200,
        '__init__': lambda self, x, y: print('object2 init', x, y),
    })

接下来,使用 Object1、Object2 类创建实例对象,注意:Object1、Object2 两个类对象是由 Meta 创建出来的。当我们使用这个类创建实例对象时候,如下代码所示:

def test():

    o1 = Object1(10, 20)
    o1.instance_function()
    o1.class_function()
    o1.static_function()

    o2 = Object2(30, 40)
    o2.instance_function()
    o2.class_function()
    o2.static_function()

此时,就会自动调用 Meta 的 call 方法来创建具体的实例对象,然后调用实例对象的 init 方法完成初始化,call 方法如下:

class Meta(type):

    # 创建实例对象
    def __call__(cls, *args, **kwargs):
        # 当使用类对象创建实例属性时,传递的参数会传递到 call 方法中
        print('Meta call', args, kwargs)
        # 调用父类 type.__call__ 方法创建实例对象
        instance = super().__call__(*args, **kwargs)
        return instance

下面是完成的案例代码:

class Meta(type):

    def __new__(cls, cname, bases, attrs):
        # 绑定类属性
        attrs['class_attr'] = 100
        # 绑定实例方法
        attrs['instance_function'] = lambda self: print('Meta 动态绑定实例方法')
        # 绑定类方法
        attrs['class_function'] = classmethod(lambda self: print('Meta 动态绑定实例方法'))
        # 绑定静态方法
        attrs['static_function'] = staticmethod(lambda : print('Meta 动态绑定静态方法'))
        # 调用父类 type.__new__ 方法创建类对象
        class_object = super().__new__(cls, cname, bases, attrs)
        print('Meta new ', cls, class_object, cname, bases, attrs)
        return class_object

    # 初始化类对象
    def __init__(cls, cname, bases, attrs):
        super().__init__(cname, bases, attrs)
        print('Meta init', cls, cname, bases, attrs)

    # 创建实例对象
    def __call__(cls, *args, **kwargs):
        # 当使用类对象创建实例属性时,传递的参数会传递到 call 方法中
        print('Meta call', args, kwargs)
        # 调用父类 type.__call__ 方法创建实例对象
        instance = super().__call__(*args, **kwargs)
        return instance


def test():

    Object1 = Meta('Object1', (), {
        'param1': 100,
        'param2': 200,
        # '__init__': lambda self: print('object1 init'),
        '__init__': lambda self, x, y: print('object1 init', x, y),
    })


    Object2 = Meta('Object2', (), {
        'param1': 100,
        'param2': 200,
        # '__init__': lambda self,: print('object2 init'),
        '__init__': lambda self, x, y: print('object2 init', x, y),
    })

    o1 = Object1(10, 20)
    o1.instance_function()
    o1.class_function()
    o1.static_function()

    o2 = Object2(30, 40)
    o2.instance_function()
    o2.class_function()
    o2.static_function()


if __name__ == '__main__':
    test()

执行结果:

Meta new  <class '__main__.Meta'> <class '__main__.Object1'> Object1 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB6157AF0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB6157940>, 'class_function': <classmethod object at 0x0000029BB61479D0>, 'static_function': <staticmethod object at 0x0000029BB616CE50>}
Meta init <class '__main__.Object1'> Object1 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB6157AF0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB6157940>, 'class_function': <classmethod object at 0x0000029BB61479D0>, 'static_function': <staticmethod object at 0x0000029BB616CE50>}
Meta new  <class '__main__.Meta'> <class '__main__.Object2'> Object2 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB63A33A0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB63A3430>, 'class_function': <classmethod object at 0x0000029BB631D580>, 'static_function': <staticmethod object at 0x0000029BB631DA00>}
Meta init <class '__main__.Object2'> Object2 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB63A33A0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB63A3430>, 'class_function': <classmethod object at 0x0000029BB631D580>, 'static_function': <staticmethod object at 0x0000029BB631DA00>}
Meta call (10, 20) {}
object1 init 10 20
Meta 动态绑定实例方法
Meta 动态绑定实例方法
Meta 动态绑定静态方法
Meta call (30, 40) {}
object2 init 30 40
Meta 动态绑定实例方法
Meta 动态绑定实例方法
Meta 动态绑定静态方法

3. metaclass

当定义类时,通过 metaclass 可以指定类对象的创建过程,这个过程定义可以是一个函数、或者带 call 方法的可调用对象。Python 在实例化对象之前,会通过这个函数或者可调用对象先把对应的类对象创建完成。

然后再调用类对象的 new、init 方法来完成最终的实例对象创建。这里需要注意的是,实例对象是由 metaclass 的 call 方法来创建的,但是 new 方法可以影响到最终创建的实例对象。在 new 方法中可以返回任意类型的实例对象,这个返回的对象会传入到 init 方法中进行初始化。

# 1. 对象创建过程
def create_class_object(cname, bases, attrs):
    print('create_class_object')
    return type(cname, bases, attrs)


class Meta(type):

    def __new__(cls, cname, bases, attrs):
        print('Meta new')
        return super().__new__(cls, cname, bases, attrs)

    def __init__(cls, cname, bases, attrs):
        print('Meta init')
        super().__init__(cname, bases, attrs)

    def __call__(cls, *args, **kwargs):
        print('Meta call')
        return super().__call__(*args, **kwargs)


# 使用 create_class_object 函数构建 MyClass1 类对象
class MyClass1(object, metaclass=create_class_object):

    class_param = 100

    def __new__(cls):
        print('MyClass1 new ')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('MyClass1 init')


# 使用 Meta 类来构建 MyClass2 类对象
class MyClass2(object, metaclass=Meta):

    class_param = 100

    def __new__(cls):
        print('MyClass2 new ')
        return super().__new__(cls)

    def __init__(self, *args, **kwargs):
        print('MyClass2 init')


def test():
    
    print('*' * 15)
    
    # 调用 MyClass1 的 new、init 方法创建对象
    object1 = MyClass1()

    print('-' * 15)

    # 调用 MyClass1 的 call、new、init 方法创建对象
    object2 = MyClass2()


if __name__ == '__main__':
    test()
create_class_object
Meta new
Meta init
***************
MyClass1 new 
MyClass1 init
---------------
Meta call
MyClass2 new 
MyClass2 init
未经允许不得转载:一亩三分地 » Python MetaClass
评论 (0)

2 + 3 =