从概念上讲,一个类的存在是为了定义一组对象(类的实例)的共同点。就这样。它允许您根据类定义的共享模式来考虑类的实例。如果每个对象都不同,我们就不会费心使用类,我们只会使用字典。
元类是一个普通的类,它的存在也是出于同样的原因;定义其实例的共同点。默认元类type
提供了使类和实例按照您习惯的方式工作的所有常规规则,例如:
- 实例上的属性查找检查实例后跟它的类,然后按照 MRO 顺序检查所有超类
- 调用
MyClass(*args, **kwargs)
调用i = MyClass.__new__(MyClass, *args, **kwargs)
来获取一个实例,然后调用i.__init__(*args, **kwargs)
来初始化它
- 通过将类块中绑定的所有名称变为类的属性,从类块中的定义创建类
- ETC
如果您想要一些与普通类不同的类,您可以定义一个元类并使您的不寻常类成为元类的实例,而不是type
. 您的元类几乎肯定是 的子类type
,因为您可能不想让您的不同类型的类完全不同;就像您可能希望让某些 Books 子集的行为有所不同(例如,作为其他作品汇编的书籍)并使用子类Book
而不是完全不同的类。
如果您不想定义一种使某些类的工作方式与普通类不同的方法,那么元类可能不是最合适的解决方案。请注意,“类定义它们的实例如何工作”已经是一个非常灵活和抽象的范例;大多数时候你不需要改变类的工作方式。
如果你四处搜索,你会看到很多元类的例子,它们实际上只是被用来做一些关于类创建的事情;通常会自动处理类属性,或者从某个地方自动查找新属性。我不会真的把这些伟大的用途称为元类。他们并没有改变类的工作方式,他们只是在处理一些类。在我看来,创建类的工厂函数,或者在类创建后立即调用的类方法,或者最好的类装饰器,将是实现这类事情的更好方法。
但有时你会发现自己编写复杂的代码来让 Python 的类的默认行为来做一些概念上简单的事情,这实际上有助于“更进一步”并在元类级别实现它。
一个相当简单的例子是“单例模式”,你有一个类,其中只能有一个实例;如果已经创建了一个实例,则调用该类将返回一个现有实例。我个人反对单例并且不建议使用它们(我认为它们只是全局变量,巧妙地伪装成看起来像新创建的实例,以便更有可能导致细微的错误)。但是人们使用它们,并且有大量使用__new__
和来制作单例类的方法__init__
。这样做可能有点烦人,主要是因为Python想调用__new__
然后调用__init__
因此,您必须找到一种方法,使您的初始化代码在每次有人请求访问单例时都不会重新运行。但是,如果我们可以在调用类时直接告诉 Python 我们想要发生什么,而不是尝试设置 Python 想要做的事情以便它们最终碰巧做我们想要做的事情,这不是更容易吗?
class Singleton(type):
def __init__(self, *args, **kwargs):
super(Singleton, self).__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton, self).__call__(*args, **kwargs)
return self.__instance
不到 10 行,只需添加 即可将普通类变成单例__metaclass__ = Singleton
,即声明它们是单例。在这个级别实现这种东西比直接在类级别上破解一些东西更容易。
但是对于您的特定Book
课程,您似乎不需要做任何可以由元类帮助的事情。你真的不需要接触元类,除非你发现类如何工作的正常规则阻止你以简单的方式做一些应该简单的事情(这不同于“伙计,我希望我不必为所有这些类输入这么多,我想知道我是否可以自动生成公共位?”)。事实上,尽管我每天在工作中都使用 Python,但我从来没有真正使用过元类。我所有的元类都是像上面这样的玩具例子,Singleton
或者只是愚蠢的探索。