我终于升级了我的python版本,我发现了添加的新功能。除此之外,我正在试着采用新的__init_subclass__
方法。来自文档:
只要包含类是子类,就会调用此方法。 CLS 那么是新的子类。如果定义为普通实例方法,则为此 方法被隐式转换为类方法。
所以我开始尝试一点,按照文档中的示例:
class Philosopher:
def __init_subclass__(cls, default_name, **kwargs):
super().__init_subclass__(**kwargs)
print(f"Called __init_subclass({cls}, {default_name})")
cls.default_name = default_name
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
pass
class GermanPhilosopher(Philosopher, default_name="Nietzsche"):
default_name = "Hegel"
print("Set name to Hegel")
Bruce = AustralianPhilosopher()
Mistery = GermanPhilosopher()
print(Bruce.default_name)
print(Mistery.default_name)
生成此输出:
Called __init_subclass(<class '__main__.AustralianPhilosopher'>, 'Bruce')
'Set name to Hegel'
Called __init_subclass(<class '__main__.GermanPhilosopher'>, 'Nietzsche')
'Bruce'
'Nietzsche'
我知道这个方法在子类定义之后被称为,但我的问题特别是关于此功能的用法。我也阅读了PEP 487文章,但对我没什么帮助。这种方法在哪里有用?是为了:
另外,我是否需要了解__set_name__
以完全理解其用法?
答案 0 :(得分:22)
PEP 487开始采用两种常见的元类用例,使其更易于访问,而无需了解元类的所有细节。这两个新功能__init_subclass__
和__set_name__
在其他地方独立,它们并不相互依赖。
__init_subclass__
只是一个钩子方法。您可以将它用于任何您想要的任何东西。它对于以某种方式注册子类非常有用,和用于在这些子类上设置默认属性值。
我们最近用它来提供适配器&#39;对于不同的版本控制系统,例如:
class RepositoryType(Enum):
HG = auto()
GIT = auto()
SVN = auto()
PERFORCE = auto()
class Repository():
_registry = {t: {} for t in RepositoryType}
def __init_subclass__(cls, scm_type=None, name=None, **kwargs):
super().__init_subclass__(**kwargs)
if scm_type is not None:
cls._registry[scm_type][name] = cls
class MainHgRepository(Repository, scm_type=RepositoryType.HG, name='main'):
pass
class GenericGitRepository(Repository, scm_type=RepositoryType.GIT):
pass
这让我们可以定义特定存储库的处理程序类,而不必使用元类或装饰器。
答案 1 :(得分:18)
__init_subclass__
和__set_name__
是正交机制 - 它们没有相互关联,只是在同一个PEP中描述。两者都是之前需要功能齐全的元类的功能。 PEP 487解决了元类最常见用法的 2 :
__init_subclass__
)__set_name__
)正如PEP所说:
虽然有许多可能的方法来使用元类,但绝大多数用例只分为三类:一些初始化代码在类创建后运行,初始化描述符并保持类属性的顺序定义强>
通过在类创建中使用简单的钩子可以很容易地实现前两个类别:
- 初始化给定类的所有子类的
__init_subclass__
挂钩。- 在创建类时,在类中定义的所有属性(描述符)上调用
__set_name__
钩子,并且第三类是另一个PEP的主题PEP 520。
另请注意,虽然__init_subclass__
是此类的继承树中使用元类的替代,但描述符类中的__set_name__
是替换为使用具有描述符实例的类的元类的元类。
答案 2 :(得分:5)
正如PEP的标题所暗示的那样,__init_subclass__
的要点是为课程提供更简单的自定义形式。
它是一个钩子,允许你修改类,无需了解元类,跟踪类构造的所有方面或担心元类冲突。在本PEP的早期阶段由Nick Coghlan提出a message:
主要的预期可读性/可维护性优势来自于 更清楚地区分“定制子类”的视角 初始化“来自”自定义运行时行为的情况 子类“case。
完整的自定义元类不提供任何范围的指示 影响,而
__init_subclass__
更清楚地表明没有 对子类创建后行为的持久影响。
因为某种原因,元类被认为是魔术,你不知道在创建类之后它们的效果是什么。另一方面,__init_subclass__
只是另一种类方法,它运行一次然后就完成了。 (see its documentation for exact functionality.)
PEP 487的重点是简化(即不再使用)元类用于某些常见用途。
__init_subclass__
处理类后初始化,而__set_name__
(仅对描述符类有意义)被添加以简化初始化描述符。除此之外,它们并不相关。
提到的元类的第三个常见情况(保持定义顺序),was also simplified。这是通过使用命名空间的有序映射(在Python 3.6中是dict
而没有钩子来解决的,但这是一个实现细节: - )
答案 3 :(得分:1)