元类

元类是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()