
Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。
当人们在想要创建一个定制类的时候,他们可能通常想到的是元类和类修饰器。在通常的会话中元类一般作为创建类的开始而类修饰符作为结束。但是您可能不知道的是在类创建中还有另外的两个步骤需要调整:__prepare__()和__init_subclass__()(分别在Python3.0和3.6中添加)。
这个__prepare__()钩子用于在构造过程中指定用于类命名空间的对象(对象被复制到一个字典中最后存储到__dict__)。该方法在元类指定,并且在__call__之前调用。历史上__prepare__()方法被用来返回OrderedDict,以便类中事物的定义顺序可以在以后知道。但是由于返回的对象被用作类的命名空间,您还可以用它来注入要在类定义中使用的对象。
从David Beazley得到了一个点子,你可以滥用__prepare__()以致你可以定义一个ABC使得abstractmethod在类定义中隐含了。

使用这个元类你可以得到abstractmethod而不用通过abc来得到它。

这种做法是因为创建类的方式本质上是通过使用类的body并将其传递给exec(),同时使用__prepare__()的结果作为区域变量。
另一种调整类创建的方式是__init_subclass__()。当定义类被子类化时,该方法被调用。它传递了提供给类定义行的子类和任何关键字参数。
为了展示一种使用这种方式的方法,我意识到你可以滥用变量类型的注释来形成一个“可怕”版本的Hynek Schlawack的attrs项目。基本上下面的类自动定义一个__init__()和(可选)__repr__()为一个基于变量类型注释的类。

这样就可以创建您可能使用的简单的Python对象types.SimpleNamespace(另外:请不要滥用collections.namedtuple来生成一个简单项目;这个类意味着帮助搬运API们来返回一个元组到一个更面向对象的API,所以开始使用namedtuple意味着你最终泄露一个你可能并不想以它开始的元组API)。

您也可以使用类定义的关键字参数来跳过__repr__()定义。

和所有调整类定义的东西一样,你一定要非常小心,不要滥用这些东西。调整怎样进行类创建可能很难调试,只有当您在有真正合法的用例的时候才能使用。但是,如果您遇到使用该代码的代码,或者您真的有需要但没有其他合理选项的时候,那么这个东西值得您了解。
Brett Cannon
Vancouver,BC,Canada
英文原文:https://snarky.ca/customizing-class-creation-in-python/
译者:liuyuexiao