3. 自定义元类

在 Python 语言中,用户可以自己定义元类,用户自定义的元类必须直接或间接的继承自 type 类。

作用

自定义元类通常用于控制此元类创建类的行为。实现自定义的功能。

语法

class 新元类(基础元类):
    ...

如:

class MyMetaClass(type):
    pass

说明

自定义元类的 可以通过覆盖 type 类的 __new____init__ 方法来控制类在创建过程中的行为。

使用元类来创建新类

语法

class 新类(基类1, 基类2, ...,  metaclass=元类):
    ...

如:

class Animal(object, metaclass=MyMetaClass):
    home = '地球'
    def print_home(self):
        print(self.__class__.home)

说明

元类的 __new____init__ 方法

  1. 元类的 __new__ 方法在类定义时执行,用于创建类并指定类属性和方法等。
  2. 元类的 __init__ 方法在新类创建后执行,用来初始化类。但魏老师没有用到过这个方法,不常用。

示例

自定义元类 MyMetaClass,此类继承自 type。

class MyMetaClass(type):
    pass

用 MyMetaClass 创建一个新类 Animal,此类继承自object,用元类 MyMetaClass 创建。

class Animal(object, metaclass=MyMetaClass):
    pass

测试打印 创建 Animal 的类和 MyMetaClass 的类都是什么?

print('创建Animal类的类是:', Animal.__class__)
print('创建MyMetaClass类的类是:', MyMetaClass.__class__)

运行结果

创建Animal类的类是: <class '__main__.MyMetaClass'>
创建MyMetaClass类的类是: <class 'type'>

改写MyMetaClass 添加 __new____init__ 方法。并打印传入的参数。如下:

class MyMetaClass(type):
    def __new__(cls, name, bases, namespace):
        print('MyMetaClass.__new__: name:', name, 'bases:', bases, 'namespace', namespace)
        return super().__new__(cls, name, bases, namespace)

    def __init__(self, name, bases, namespace):
        print('MyMetaClass.__init__: name:', name, 'bases:', bases, 'namespace', namespace)
        super().__init__(name, bases, namespace)


class Animal(object, metaclass=MyMetaClass):
    pass

运行结果如下:

MyMetaClass.__new__: name: Animal bases: (<class 'object'>,) namespace {'__module__': '__main__', '__qualname__': 'Animal', '__firstlineno__': 15, '__static_attributes__': ()}
MyMetaClass.__init__: name: Animal bases: (<class 'object'>,) namespace {'__module__': '__main__', '__qualname__': 'Animal', '__firstlineno__': 15, '__static_attributes__': ()}

可见,在创建类 Animal 时 调用了 __new____init__ 方法。并传入了类 Animal的名称、继承元组和属性信息。

修改 Animal 类如下,添加了两个类属性。继续运行程序

class Animal(object, metaclass=MyMetaClass):
    home = '地球'
    def print_home(self):
        print(self.__class__.home)

运行结果如下:

MyMetaClass.__new__: name: Animal bases: (<class 'object'>,) namespace {'__module__': '__main__', '__qualname__': 'Animal', '__firstlineno__': 15, 'home': '地球', 'print_home': <function Animal.print_home at 0x1091c45e0>, '__static_attributes__': ()}
MyMetaClass.__init__: name: Animal bases: (<class 'object'>,) namespace {'__module__': '__main__', '__qualname__': 'Animal', '__firstlineno__': 15, 'home': '地球', 'print_home': <function Animal.print_home at 0x1091c45e0>, '__static_attributes__': ()}

可见 Animal 类中的两个类属性也会以键值对的形式传递给元类 的 __new____init__ 的 第四个参数 namespace。

最后再创建一个Animal类的子类,在创建此类时依旧会调用元类 MyMetaClass 中的 __new____init__ 方法。代码如下

class MyMetaClass(type):
    def __new__(cls, name, bases, namespace):
        print('MyMetaClass.__new__: name:', name, 'bases:', bases, 'namespace', namespace)
        return super().__new__(cls, name, bases, namespace)

    def __init__(self, name, bases, namespace):
        print('MyMetaClass.__init__: name:', name, 'bases:', bases, 'namespace', namespace)
        super().__init__(name, bases, namespace)


class Animal(object, metaclass=MyMetaClass):
    home = '地球'
    def print_home(self):
        print(self.__class__.home)

class Dog(Animal):
    pass

print('创建Animal类的类是:', Animal.__class__)
print('创建MyMetaClass类的类是:', MyMetaClass.__class__)

运行结果

MyMetaClass.__new__: name: Animal bases: (<class 'object'>,) namespace {'__module__': '__main__', '__qualname__': 'Animal', '__firstlineno__': 15, 'home': '地球', 'print_home': <function Animal.print_home at 0x1091c45e0>, '__static_attributes__': ()}
MyMetaClass.__init__: name: Animal bases: (<class 'object'>,) namespace {'__module__': '__main__', '__qualname__': 'Animal', '__firstlineno__': 15, 'home': '地球', 'print_home': <function Animal.print_home at 0x1091c45e0>, '__static_attributes__': ()}
MyMetaClass.__new__: name: Dog bases: (<class '__main__.Animal'>,) namespace {'__module__': '__main__', '__qualname__': 'Dog', '__firstlineno__': 21, '__static_attributes__': ()}
MyMetaClass.__init__: name: Dog bases: (<class '__main__.Animal'>,) namespace {'__module__': '__main__', '__qualname__': 'Dog', '__firstlineno__': 21, '__static_attributes__': ()}
创建Animal类的类是: <class '__main__.MyMetaClass'>
创建MyMetaClass类的类是: <class 'type'>