#!/usr/bin/env python# -*- coding: utf-8 -*-# 你的程序包含一个很大的类继承体系,你希望强制执行某些编程规约(或者代码诊断)来帮助程序员保持清醒。# 如果你想监控类的定义,通常可以通过定义一个元类。一个基本元类通常是继承自 type 并重定义它的 __new__() 方法 或者是 __init__() 方法。比如:# 方法一class MyMeta(type):def __new__(cls, clsname, bases, clsdict):return super().__new__(cls, clsname, bases, clsdict)# 方法二class MyMeta2(type):def __init__(self, clsname, bases, clsdict):super().__init__(clsname, bases, clsdict)# 为了使用这个元类,你通常要将它放到到一个顶级父类定义中,然后其他的类继承这个顶级父类。例如:class Root(metaclass=MyMeta):passclass A(Root):passclass B(Root):pass# 作为一个具体的应用例子,下面定义了一个元类,它会拒绝任何有混合大小写名字作为方法的类定义(可能是想气死Java程序员^_^):class NoMixedCaseMeta(type):def __new__(cls, clsname, bases, clsdict):for name in clsdict:if name.lower() != name:raise TypeError('Bad attribute name: ' + name)return super().__new__(cls, clsname, bases, clsdict)class Root(metaclass=NoMixedCaseMeta):passclass A1(Root): # Okdef foo_bar(self):passclass B1(Root): # TypeErrordef fooBar(self):pass
讨论
大型面向对象的程序中,通常将类的定义放在元类中控制是很有用的。 元类可以监控类的定义,警告编程人员某些没有注意到的可能出现的问题。
有人可能会说,像这样的错误可以通过程序分析工具或IDE去做会更好些。诚然,这些工具是很有用。 但是,如果你在构建一个框架或函数库供其他人使用,那么你没办法去控制使用者要使用什么工具。 因此,对于这种类型的程序,如果可以在元类中做检测或许可以带来更好的用户体验。
在元类中选择重新定义 __new__() 方法还是 __init__() 方法取决于你想怎样使用结果类。__new__() 方法在类创建之前被调用,通常用于通过某种方式(比如通过改变类字典的内容)修改类的定义。 而 __init__() 方法是在类被创建之后被调用,当你需要完整构建类对象的时候会很有用。 在最后一个例子中,这是必要的,因为它使用了 super() 函数来搜索之前的定义。 它只能在类的实例被创建之后,并且相应的方法解析顺序也已经被设置好了。
