元类是类的类,类是对象的模板,元类是类的模板,元类是用来创建类的,就像类是用来创建对象的一样。在 Python 中,一切皆对象,那么类也是对象,正如你用类来创建对象一样,也可以使用元类来创建类。
type
函数
我们知道 Python 中,一切皆对象,同样的道理,那么类也是对象,既然是对象,那么是什么类型呢?type
函数不是就可以返回一个对象的类型吗?那么我们来看看类的类型是什么:
class Student: pass print(type(Student)) # <class 'type'>
从上面输出结果可以看出,类的类型是 type
,这就是元类,元类是用来创建类的类,type
函数既可以返回一个对象的类型,也可以创建新的类型,也就是我们无需通过 class
关键字就可以定义类了,比如我们想要创建出 Student
类,可以通过 type
函数来创建。
如果类中包含方法,那么我们可以先定义该函数:
def print_hello(self): print(f"Hello, {self.name}!")
然后使用 type()
函数就可以创建 Student
类了:
Student = type('Student', (object,), dict(name='James', hello=print_hello)) s = Student() s.hello() # 输出: Hello, James! print(type(Student)) # <class 'type'> print(type(s)) # <class '__main__.Hello'>
上面代码中我们就通过 type()
函数创建了一个 Student
类,type()
函数依次传入 3 个参数:
- class 的名称
- 继承的父类集合,是一个 tuple 类型,也就是可以继承多个父类
- 第三个参数是一个包含属性或方法的字典
通过 type()
函数创建的类和直接用 class
关键字定义的类是完全一样的,事实上 Python 解释器遇到 class
定义时,仅仅也只是扫描一下 class
定义的语法,然后也是调用 type()
函数来创建出类。
当然我们也可以使用 lambda
函数来定义方法:
Student = type('Student', (object,), dict(name='James', hello=lambda self: f"Hello, {self.name}!"))
自定义元类
除了使用 type()
动态创建类以外,要控制类的创建行为,还可以使用 metaclass
,也就是所谓的元类,元类允许你创建类或者修改类,换句话说,我们可以把类看成是元类创建出来的“实例”。
因为 metaclass
是类的模板,所以必须从 type
类型派生,比如现在我们来定义一个如下所示的元类:
# 定义一个元类,它继承自 type class Meta(type): """__new_方法的参数: cls:当前准备创建的类的对象 name:类的名字 bases:类继承的父类集合 dct:类的方法集合 """ def __new__(cls, name, bases, dct): # 在创建类时自动添加新的方法 dct['greet'] = lambda self: f"Hello from {name}" return super().__new__(cls, name, bases, dct)
在上面代码中我们定义了一个 Meta
类,继承自 type
,也就是说明了该类是一个元类,然后我们在该类中定义了一个 __new__
的方法,该方法是用来定义如何实例化一个类的,比如我们这里定义了一个 greet
方法,也就是在创建类的时候会自动添加这个新的方法。需要注意的是如果该方法不返回任何值(或者说不返回对象实例),那么 __init__
方法将不会被调用。
有了 Meta
元类后,我们在定义类的时候还要指示使用这个元类来定制类,需要传入关键字参数 metaclass
:
# 使用元类创建类 class MyClass(metaclass=Meta): x = 5 # 创建一个实例 instance = MyClass() print(instance.greet()) # 输出: Hello from MyClass
当我们传入关键字参数 metaclass
时,它指示 Python 解释器在创建 MyClass
时,要通过 Meta.__new__()
来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
那么这样去动态修改有什么意义呢?我们直接在类中定义这些方法不是更简单吗?当然正常情况下我们是不会去使用 metaclass
的,但还是有一些场景需要通过 metaclass
修改类定义的,最典型的一个场景就是 ORM
。ORM
全称 Object Relational Mapping
,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作 SQL 语句。要编写一个 ORM
框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来,这个我们在后面的课程中会详细讲解。