1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # 你的程序包含一个很大的类继承体系,你希望强制执行某些编程规约(或者代码诊断)来帮助程序员保持清醒。
  4. # 如果你想监控类的定义,通常可以通过定义一个元类。一个基本元类通常是继承自 type 并重定义它的 __new__() 方法 或者是 __init__() 方法。比如:
  5. # 方法一
  6. class MyMeta(type):
  7. def __new__(cls, clsname, bases, clsdict):
  8. return super().__new__(cls, clsname, bases, clsdict)
  9. # 方法二
  10. class MyMeta2(type):
  11. def __init__(self, clsname, bases, clsdict):
  12. super().__init__(clsname, bases, clsdict)
  13. # 为了使用这个元类,你通常要将它放到到一个顶级父类定义中,然后其他的类继承这个顶级父类。例如:
  14. class Root(metaclass=MyMeta):
  15. pass
  16. class A(Root):
  17. pass
  18. class B(Root):
  19. pass
  20. # 作为一个具体的应用例子,下面定义了一个元类,它会拒绝任何有混合大小写名字作为方法的类定义(可能是想气死Java程序员^_^):
  21. class NoMixedCaseMeta(type):
  22. def __new__(cls, clsname, bases, clsdict):
  23. for name in clsdict:
  24. if name.lower() != name:
  25. raise TypeError('Bad attribute name: ' + name)
  26. return super().__new__(cls, clsname, bases, clsdict)
  27. class Root(metaclass=NoMixedCaseMeta):
  28. pass
  29. class A1(Root): # Ok
  30. def foo_bar(self):
  31. pass
  32. class B1(Root): # TypeError
  33. def fooBar(self):
  34. pass

讨论

大型面向对象的程序中,通常将类的定义放在元类中控制是很有用的。 元类可以监控类的定义,警告编程人员某些没有注意到的可能出现的问题。

有人可能会说,像这样的错误可以通过程序分析工具或IDE去做会更好些。诚然,这些工具是很有用。 但是,如果你在构建一个框架或函数库供其他人使用,那么你没办法去控制使用者要使用什么工具。 因此,对于这种类型的程序,如果可以在元类中做检测或许可以带来更好的用户体验。

在元类中选择重新定义 __new__() 方法还是 __init__() 方法取决于你想怎样使用结果类。__new__() 方法在类创建之前被调用,通常用于通过某种方式(比如通过改变类字典的内容)修改类的定义。 而 __init__() 方法是在类被创建之后被调用,当你需要完整构建类对象的时候会很有用。 在最后一个例子中,这是必要的,因为它使用了 super() 函数来搜索之前的定义。 它只能在类的实例被创建之后,并且相应的方法解析顺序也已经被设置好了。