当前位置: 首页 > 面试题库 >

了解__init_subclass__

邵畅
2023-03-14
问题内容

我终于升级了python版本,并发现了新功能。除其他外,我正在为新__init_subclass__方法scratch之以鼻。从文档:

只要包含类被子类化,就会调用此方法。那么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__才能完全理解其用法?


问题答案:

__init_subclass__并且__set_name__是正交机制-
它们并不相互关联,只是在同一PEP中进行了描述。两者都是以前需要功能齐全的元类的功能。该PEP 487地址 2 元类最常见的用途:

  • 如何让父母知道何时将其子类化(__init_subclass__
  • 如何让描述符类知道用于(__set_name__)的属性的名称

正如PEP所说:

尽管有很多使用元类的方法,但 绝大多数用例可分为三类:在类创建后运行的一些初始化代码,描述符的初始化以及保持类属性定义的顺序。

通过对类的创建进行简单的挂钩,就可以轻松实现前两个类别:

  • 一个__init_subclass__钩子,用于初始化给定类的所有子类。
  • 创建__set_name__类时,将对类中定义的所有属性(描述符)调用一个钩子,并且

第三类是另一个PEP 520的主题。

还要注意,虽然__init_subclass__可以替换在 此类 的继承树中使用元类,但是__set_name__描述符类
中的替换是针对将 描述符 的实例 作为attribute 的类使用元类。



 类似资料:
  • 问题内容: 我不太清楚该在哪里抛出该异常。 例如,我正在实现接口,并且不希望任何人调用该方法: Future#get(long,TimeUnit)。 所以,我可以扔吗? 事情是方法的规范并没有说明抛出异常。反过来,例外 抛出以指示不支持请求的操作。 类UnsupportedOperationException 我的意思是,如果您不希望调用它,通常会抛出它,否则可能会因为不是所有方法都已实现而被认为

  • 问题内容: 我试图了解Go中的并发性。特别是,我编写了以下线程不安全程序: 我认识到我应该使用渠道来防止与发生竞争,但这不是重点。程序打印,然后似乎永远循环(不再打印任何内容)。我希望它能打印出无限的数字列表,可能由于竞态条件而跳过某些数字并重复其他数字(或更糟的是,在更新数字时打印数字)。 我的问题是:为什么程序只打印一行? 只是要清楚一点:对于这个玩具示例,我不是故意使用渠道。 问题答案: 关

  • 问题内容: 我了解这是一个框架;甚至还有一个开源的跨平台游戏开发库。我转到libgdx主页,并按照视频教程中的说明进行操作。正确设置项目后,我可以在多个受支持的平台上运行默认的my- gdx-game项目。太好了,花花公子…现在呢? 我一直在搜索论坛,Wiki,javadocs和许多其他站点,以寻找体面的简单操作方法。不幸的是,我找不到任何东西,那里的大多数帮助都假设您对这个库有一些基本的了解。

  • 问题内容: JNDI就像是类固醇的地图,对吗?我使用键来查找对对象的引用。另外,什么是InitialContext?我似乎不知道这个主意。 问题答案: 从概念上讲,JNDI就像类固醇。 允许您从命令行将参数传递给代码。同样,JNDI允许您在代码外部(例如,在应用程序服务器配置文件中)配置任意对象,然后在代码中使用它们。 换句话说,它是Service Locator模式 的实现:您的代码从中心注册表

  • 问题内容: 我正在尝试使用触发两个请求,然后在两个请求完成后调用一些函数。这是我的代码: 但是,这没有按预期方式工作。Ajax调用将返回要在其中使用的数据,而Ajax调用将返回要分配给var count并随后在中使用的count 。 但是,当我启动上面的代码时,先被调用,然后再将数据保留为as 。我如何才能做到这一点,据我所知,只有当两个返回的函数都执行时,才能进行。我希望两个ajax调用都应该并

  • 问题内容: 我遇到了“ 高级Linux编程”中的 一个概念。这里是一个链接:请参阅 4.5 GNU / Linux线程实现 。 我对作者所说的概念很清楚,但是我对他解释的为线程打印processID的程序感到困惑。 这是代码 根据作者,上述代码的输出为 我编译时得到的输出是 我知道,创建线程时,Linux内部调用 clone (大多数情况下),就像 fork 系统调用创建进程一样。唯一的区别是在进