在 Python 中实例化时选择子类

Posted

技术标签:

【中文标题】在 Python 中实例化时选择子类【英文标题】:Selecting a subclass at instantiation in Python 【发布时间】:2020-03-31 23:03:42 【问题描述】:

我想在实例化时选择一个子类以从某些类中选择特定属性。我已经解决了以下问题:

代码

class Foo:
    a = "foo"

    def __init__(self, dtype=Bar):
            self.__class__ = dtype


class Bar:
    b = "bar"


class Baz(Bar):
    c = "baz"

演示

Foo(dtype=Bar).b
# 'bar'

Foo(dtype=Baz).b
# 'bar'

Foo(dtype=Baz).c
# 'baz'

这给出了所需的结果,从Bar 中选择特定属性,同时可以选择使用Baz 扩展功能。然而,与子类化不同的是,我们无法访问Foo 的属性。

Foo(dtype=Baz).a
# AttributeError: 'Baz' object has no attribute 'a'

在某些情况下,并非所有属性都需要,因此子类化 Foo(Baz) 不是首选。

在 Python 中实现此目的的惯用模拟是什么?

【问题讨论】:

为什么不能只使用继承?您可以动态选择子类,例如var = Bar; var() 这听起来像XY problem。我不知道这样做会不会是惯用的。 @KentShikama 选择var = Bar() 仍然排除Foo 中的属性。 每docs.python.org/3/reference/datamodel.html#object.__new__,如果__new__返回一个实例(包括子类),__init__将被调用。但这都是type.__call__执行的;另见***.com/questions/6966772/… 也就是说,__new__ 返回不同的类型应该很少见;我只在 FFI 相关代码中将NULL 映射到None 【参考方案1】:

如果您真的想要BarBaz 的实例,为什么首先要创建Foo 的实例?相反,将Foo 设为BarBaz 的工厂。

class Bar:
    b = "bar"

class Baz(Bar):
    c = "baz"

class Foo:
    a = "foo"

    def __new__(cls, dtype=Bar):
        return dtype()

【讨论】:

【参考方案2】:

正如我在 cmets 中提到的,只需使用普通继承即可。

class Foo:
    a = "foo"

class Bar(Foo):
    b = "bar"

class Baz(Foo):
    c = "baz"

# Example use
import random
class_to_instantiate = Bar if random.choice([True, False]) else Baz
print(class_to_instantiate().a)

输出

foo

【讨论】:

以上是关于在 Python 中实例化时选择子类的主要内容,如果未能解决你的问题,请参考以下文章

当子类被实例化时,超类的私有成员是不是也被实例化? [复制]

在实例化时将类转换为子类

类加载器实例化时的顺序

类加载器实例化时的顺序

Java继承,子类实例化时,调用父类的无参构造方法

当子类使用Object变量实例化时,无法从超类访问方法