python元类

在 Python 中,一切皆对象,类也不例外。我们通常认为类是用来创建实例的“模版”,但实际上,类本身也是对象。也就是说,类是由某种机制动态生成的,这个机制就是 元类(metaclass)

一、类也是对象

当你使用 class 关键字定义一个类时,Python 会根据这个类的描述信息,创建一个对象。这个对象本身就是一个“类”,但它也是某个元类的实例:

1
2
3
4
5
6
7
8
9
10
>>> class ObjectCreator(object):
... pass

>>> JustAnotherVariable = ObjectCreator
>>> ObjectCreator.class_attribute = 'foo'

>>> print(ObjectCreator)
<class '__main__.ObjectCreator'>
>>> print(JustAnotherVariable)
<class '__main__.ObjectCreator'>

如上所示,类 ObjectCreator 是一个对象,可以赋值给变量、添加属性等,就像其他对象一样。

二、谁创建了类?

既然类本身是对象,那它也必须是由某种“类”创建的。Python 默认使用 type 这个内建元类来创建所有类:

1
2
3
4
>>> print(type(1))               # <class 'int'>
>>> print(type("1")) # <class 'str'>
>>> print(type(ObjectCreator)) # <class 'type'>
>>> print(type(ObjectCreator())) # <class '__main__.ObjectCreator'>

这说明:ObjectCreator 是由 type 创建的对象(类),而 ObjectCreator() 是该类创建的实例对象。

三、使用 type 动态创建类

type 不只是一个用于检查对象类型的函数,它还可以动态地创建类。其使用方式如下:

1
2
3
4
type(name, bases, attrs)
# name: 类名
# bases: 父类的元组
# attrs: 类属性的字典

示例:

1
2
3
4
5
>>> Foo = type('Foo', (), {})
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo())
<__main__.Foo object at 0x...>

你还可以在创建类时直接添加方法:

1
2
3
4
5
6
7
8
9
>>> def echo_bar(self):
... print(self.bar)

>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})

>>> my_foo = FooChild()
>>> my_foo.bar = "hello"
>>> my_foo.echo_bar()
hello

并且你可以像普通类一样,动态添加更多方法:

1
2
3
4
5
6
>>> def echo_bar_more(self):
... print('yet another method')

>>> FooChild.echo_bar_more = echo_bar_more
>>> my_foo.echo_bar_more()
yet another method

四、什么是元类(Metaclass)?

简单来说,元类是“用于创建类的类”

类是实例的工厂,而元类就是类的工厂。Python 使用 type 作为默认的元类,但你也可以自定义自己的元类来控制类的创建过程。

当你定义一个类时,如果指定了 metaclass=XXX,Python 会使用你提供的元类去创建这个类:

1
2
3
4
5
6
7
8
9
10
11
class MetaClass(type):
def __init__(cls, name, bases, attrs):
print(f'class name: {name}')
print(f'Defining class {cls}')
print(f'Bases: {bases}')
print('Attributes:')
for key, value in attrs.items():
print(f'{key}: {value}')

class Foo(object, metaclass=MetaClass):
pass

运行结果:

1
2
3
4
5
6
class name: Foo
Defining class <class '__main__.Foo'>
Bases: (<class 'object'>,)
Attributes:
__module__: '__main__'
__qualname__: 'Foo'

注意:这里的 MetaClass.__init__ 会在类定义时执行,而不是类实例化时。

五、元类的典型应用场景

元类常用于构建框架或库中,例如:

  • 自动注册类
  • 为所有类添加统一方法或属性
  • 控制属性的访问或修改
  • 实现ORM字段与数据库字段的映射逻辑

六、示例

1.要求一个类的所有属性都变成大写。先用一个函数来实现这个功能(metaclass的输入可以是函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def upper_attr(future_class_name, future_class_parents, future_class_attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
}
return type(future_class_name, future_class_parents, uppercase_attrs)

class ClassC(metaclass=upper_attr):
attrib = 'some value'

c1 = ClassC()
# False
print(hasattr(c1, 'attrib'))
# True
print(hasattr(c1, 'ATTRIB'))
#
print(c1.ATTRIB)

2.控制成员访问
以下是一个完整示例,我们自定义了一个元类,用来控制对类属性的访问,禁止对“私有属性”的写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class MetaMemberControl(type):
def __new__(mcs, name, bases, attrs):
original_getattr = attrs.get('__getattribute__')
original_setattr = attrs.get('__setattr__')

def init_getattr(self, item):
if not item.startswith('_'):
alias_name = '_' + item
if alias_name in attrs.get('__slots__', ()):
item = alias_name
if original_getattr:
return original_getattr(self, item)
return super(eval(name), self).__getattribute__(item)

def init_setattr(self, key, value):
if not key.startswith('_') and ('_' + key) in attrs.get('__slots__', ()):
raise AttributeError(f"you can't modify private member: _{key}")
if original_setattr:
return original_setattr(self, key, value)
return super(eval(name), self).__setattr__(key, value)

attrs['__getattribute__'] = init_getattr
attrs['__setattr__'] = init_setattr

return super().__new__(mcs, name, bases, attrs)

class Human(metaclass=MetaMemberControl):
__slots__ = ('_age', '_name')

def __init__(self, name, age):
self._name = name
self._age = age

测试代码如下:

1
2
3
4
5
def test_demo():
h = Human('Alice', 30)
print(h.age) # 可以访问(实际上访问的是 _age)
print(h._age) # 也可以直接访问(Python不限制)
h.age = 20 # 抛出 AttributeError

元类帮我们自动生成了简化的访问逻辑,无需手写 @property

七、总结

元类是 Python 中极其强大的功能,能够对类的创建过程进行控制,是实现高级抽象与框架功能的核心工具之一。其他可用于类变更的方法更直接,应该优先考虑,比如monkey补丁和类装饰器。

在日常开发中,可能很少直接使用元类,但理解它的工作原理将有助于我们更深入地掌握Python的对象模型,尤其是在阅读高级框架如Django、SQLAlchemy或Pydantic时。


python元类
https://vegetablest.github.io/2025/05/29/python元类/
作者
af su
发布于
2025年5月29日
许可协议