元类
元类是Python中的一个重要特性,要理解其工作方式,必须要明确用于建立对象实例的类也是对象,那么就一定有用于建立类的相关类。在Python中,定义所有类的基类都是内置的type
类。
例如,运行以下示例代码,可以看到类的类型。
class MyClass:
pass
print(type(MyClass))
使用type()
创建类
type()
作为class
的等效功能,给定类名、基类名和属性映射即可创建一个新类。具体创建方法可以参考以下示例。
def foo(self):
print("From foo")
def boo(self):
print("From boo")
DynamicClass = type("DynamicClass", (object, ), {"func1": foo, "func2": boo})
obj = DynamicClass()
print(type(obj))
obj.func1()
元类模板
使用class
创建的类都隐式默认使用type()
作为元类,在定义类的时候,可以使用metaclass
来指定要使用的元类来改变Python的默认行为。
前面面向对象一章曾经介绍过Python中的魔术方法,在定义元类时也常常使用魔术方法来定义元类模板。元类模板中所使用的魔术方法与一般类中的魔术方法有所不同,以下表格中列出了元类模板定义常用的魔术方法,这些魔术方法也可以被使用到一般类的定义中,并不局限于元类模板中使用。
魔术方法 | 含义与使用方法 |
---|---|
__prepare__(metacls, name, bases, **kwargs) | 用于创建一个空的命名空间,传递给__new__ ,返回一个空的字典。先于__new__ 调用。 |
__new__(cls, []) | __new__ 是对象实例化时调用的第一个方法,其第一个参数为要实例化的这个类本身,其他的参数是要直接传递给__init__ 方法的。__new__ 可以决定是否要调用__init__ 方法,或者调用其他类的构造方法,亦或是返回其他类的实例来作为本类的实例。__new__ 没有返回实例对象,则__init__ 就不会被调用。 |
__init__(self, []) | 一个实例被创建后调用的初始化方法。 |
__del__(self) | 析构方法,一个实例被销毁时调用的方法。 |
__call__(self[, args]) | 允许一个类的实例像函数一样被调用,例如x(a, b) 可以是x.__call__(a, b) 的形式。 |
__getattr__(self, name) | 定义当用户试图获取一个不存在的属性时要执行的行为。 |
__getattribute__(self, name) | 定义当该类属性被访问时的行为。 |
__setattr__(self, name, value) | 定义当一个属性被设置时的行为。 |
__delattr__(self, name) | 定义当一个属性被删除时的行为。 |
__dir__(self) | 定义当dir() 被调用时的行为。 |
__get__(self, instance, owner) | 定义当描述符被获取时的行为。 |
__set__(self, instance, value) | 定义当描述符的值被改变时的行为。 |
__delete__(self, instance) | 定义当描述符的值被删除时的行为。 |
有了以上这些魔术方法,就可以仿照以下示例来定义和使用元类模板。
class MetaClassTemplate(type):
""" 注意元类模板中的魔术方法接受的第一个参数大多都为一个类,而不是一个实例 """
@classmethod
def __prepare__(metacls, name, bases, **kwargs):
""" __prepare__方法的第一个参数要接受一个元类,所以需要使用@classmethod修饰 """
print("Meta Class Template __prepare__")
return super().__prepare__(name, bases, **kwargs)
def __new__(metacls, name, bases, namespace):
print("Meta Class Template __new__")
return super().__new__(metacls, name, bases, namespace)
def __init__(cls, name, bases, namespace, **kwargs):
print("Meta Class Template __init__")
super().__init__(name, bases, namespace)
def __call__(cls, *args, **kwargs):
print("Meta Class Template __call__")
return super().__call__(*args, **kwargs)
class ANewClass(metaclass=MetaClassTemplate):
def __new__(cls):
print("New Class __new__")
return super().__new__(cls)
def __init__(self):
print("New Class __init__")
super().__init__()
# 可以观察一下以上元类模板和使用元类定义的类的执行顺序
obj = ANewClass()