【问题标题】:How to instantiate a subclass type variable from an existing superclass type object in Python如何在 Python 中从现有的超类类型对象实例化子类类型变量
【发布时间】:2020-07-27 14:55:56
【问题描述】:

我有一种情况,我用几个属性扩展了一个类:

class SuperClass:
    def __init__(self, tediously, many, attributes):
        # assign the attributes like "self.attr = attr"

class SubClass:
    def __init__(self, id, **kwargs):
        self.id = id
        super().__init__(**kwargs)

然后我想创建实例,但我知道这会导致子类只能像这样实例化:

super_instance = SuperClass(tediously, many, attributes)

sub_instance = SubClass(id, tediously=super_instance.tediously, many=super_instance.many, attributes=super_instance.attributes)

我的问题是,是否可以通过复制超类实例的属性来实例化子类更漂亮/更简洁,而无需编写一段香肠代码来手动执行它(在构造函数调用或构造函数的主体中) )... 类似:

utopic_sub_instance = SubClass(id, **super_instance)

【问题讨论】:

  • 这能回答你的问题吗? What are "inheritable alternative constructors"?
  • @mkrieger1 不是真的,如果我能以某种方式将 super_instance 传递给 SubClass 的构造函数并让它填充从 SuperClass 继承的字段,我很感兴趣。我基本上是想避免必须手动分配从 SuperClass 继承的许多属性
  • 是的,您可以通过编写一个可以调用的替代构造函数来做到这一点,如SubClass.from_superclass(super_instance, id) - 如何编写替代构造函数在另一个问题中显示。
  • @mkrieger1 但我不必在这个 alt 构造函数中手动将 super_instance 中的属性值分配给 SubClass 的 self 属性吗?
  • 是的,或者这可能会有所帮助:stackoverflow.com/questions/1241148/copy-constructor-in-python

标签: python python-3.x oop instantiation subclassing


【解决方案1】:

也许你想要一些关于如何不写这么多代码的具体想法? 所以一种方法是这样的:

class A:
    def __init___(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


class B:
    def __init__(self, x, a, b, c):
        self.x = x
        super().__init__(a, b, c)


a = A(1, 2, 3)
b = B('x', 1, 2, 3)


# so your problem is that you want to avoid passing 1,2,3 manually, right?
# So as a comment suggests, you should use alternative constructors here.
# Alternative constructors are good because people not very familiar with
#  Python could also understand them.
# Alternatively, you could use this syntax, but it is a little dangerous and prone to producing 
# bugs in the future that are hard to spot


class BDangerous:
    def __init__(self, x, a, b, c):
        self.x = x
        kwargs = dict(locals())
        kwargs.pop('x')
        kwargs.pop('self')

        # This is dangerous because if in the future someone adds a variable in this 
        # scope, you need to remember to pop that also
        # Also, if in the future, the super constructor acquires the same parameter that
        # someone else adds as a variable here... maybe you will end up passing an argument
        # unwillingly. That might cause a bug
        # kwargs.pop(...pop all variable names you don't want to pass)
        super().__init__(**kwargs)


class BSafe:
    def __init__(self, x, a, b, c):
        self.x = x
        bad_kwargs = dict(locals())

        # This is safer: you are explicit about which arguments you're passing
        good_kwargs = {}
        for name in 'a,b,c'.split(','):
            good_kwargs[name] = bad_kwargs[name]

        # but really, this solution is not that much better compared to simply passing all 
        # parameters explicitly
        super().__init__(**good_kwargs)
  

或者,让我们更疯狂一点。我们将使用自省来动态构建要作为参数传递的 dict。我的示例中没有包含仅关键字参数、默认值、*args 或 **kwargs 的情况

class A:
    def __init__(self, a,b,c):
        self.a = a
        self.b = b
        self.c = c


class B(A):
    def __init__(self, x,y,z, super_instance):
        import inspect
        spec = inspect.getfullargspec(A.__init__)

        positional_args = []
        super_vars = vars(super_instance)

        for arg_name in spec.args[1:]:  # to exclude 'self'
            positional_args.append(super_vars[arg_name])

        # ...but of course, you must have the guarantee that constructor
        # arguments will be set as instance attributes with the same names
        super().__init__(*positional_args)

【讨论】:

  • 哦,所以这里真的没有办法偷懒……不幸的是。可能使用带有显式传递参数的替代构造函数是要走的路
  • instance_b = Subclass.from_super_instance(arg1, arg2, super_instance=instance_a)... 然后你实现 from_super_instance 为你设置字段:P
【解决方案2】:

我最终成功地使用了 alt 构造函数和 super_instance 的 __dict__ 属性的组合。

class SuperClass:
    def __init__(self, tediously, many, attributes):
        self.tediously = tediously 
        self.many = many 
        self.attributes = attributes

class SubClass(SuperClass):
    def __init__(self, additional_attribute, tediously, many, attributes):
        self.additional_attribute = additional_attribute
        super().__init__(tediously, many, attributes)

    @classmethod
    def from_super_instance(cls, additional_attribute, super_instance):
        return cls(additional_attribute=additional_attribute, **super_instance.__dict__)

super_instance = SuperClass("tediously", "many", "attributes")

sub_instance = SubClass.from_super_instance("additional_attribute", super_instance)

注意:请记住,python 是按顺序执行语句的,因此如果您想覆盖继承属性的值,请将super().__init__() 放在SubClass.__init__ 中的其他赋值语句之前。

注意 2:pydantic 有这个非常好的特性,他们的 BaseModel 类自动生成 .__init__() 方法,有助于属性类型验证并为此类模型提供 .dict() 方法(它与 @ 基本相同987654329@ 虽然)。

【讨论】:

    猜你喜欢
    • 2013-10-22
    • 2015-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-20
    • 1970-01-01
    • 2016-06-13
    • 2015-05-29
    相关资源
    最近更新 更多