在 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 )) >>> print (type ("1" )) >>> print (type (ObjectCreator)) >>> print (type (ObjectCreator()))
这说明:ObjectCreator
是由 type
创建的对象(类),而 ObjectCreator()
是该类创建的实例对象。
三、使用 type
动态创建类 type
不只是一个用于检查对象类型的函数,它还可以动态地创建类。其使用方式如下:
1 2 3 4 type (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
简单来说,元类是“用于创建类的类” 。
类是实例的工厂,而元类就是类的工厂。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 Bases: (<class Attributes: __module__: __qualname__:
注意:这里的 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()print (hasattr (c1, 'attrib' ))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) print (h._age) h.age = 20
元类帮我们自动生成了简化的访问逻辑,无需手写 @property
。
七、总结 元类是 Python 中极其强大的功能,能够对类的创建过程进行控制,是实现高级抽象与框架功能的核心工具之一。其他可用于类变更的方法更直接,应该优先考虑,比如monkey补丁和类装饰器。
在日常开发中,可能很少直接使用元类,但理解它的工作原理将有助于我们更深入地掌握Python的对象模型,尤其是在阅读高级框架如Django、SQLAlchemy或Pydantic时。