如何从父类实例化子类

3
我希望能从父类创建一个子类,以减少代码冗余。例如:
class Parent():
   def __init__(self, a, b, c, d, e, f, g):
       self.a = a
       self.b = b
       ...
       self.g = g

class Child1(Parent):
   def __init__(self, a, b, c, d, e, f, g, h, i, j, k):
          super().__init__(a, b, c d, e, f, g)
          self.i = i
          self.j = j
          self.k = k

class Child2(Parent):
   def __init__(self, a, b, c, d, e, f, g, h, x, y, z):
          super().__init__(a, b, c d, e, f, g)
          self.x = x
          self.y = y
          self.z = z

我不想为所有子类再次传递参数。有没有一种方法可以从父类创建Child1和Child2?
我有30多个参数和许多子类。写出所有参数似乎非常冗余。而且,它们都与父类共享相同的参数。

可能是 Simple python inheritance 的重复问题。 - rafaelc
1
如果您有30个以上的参数,那么您的代码设计肯定存在问题。您是否考虑过传递一个字典? - DYZ
嗯,这是一堆我需要调整的超参数,用于深度学习方面的东西... - MoneyBall
您可能正在寻找一些略有不同但仍涉及工厂或装饰器创建或协助创建类的东西。如果是这样,阅读这些库(以及一些更简单的库,比如 functools.total_ordering)的源代码可能会向您展示不同的选项,即使它们不是您需要的开箱即用选项。 - abarnert
@MoneyBall "Instantiate" 的意思是创建一个属于类的实例化对象。因此,当你创建一个类时,你实例化的东西就是元类。但这几乎肯定与本例无关 —— 所有的类都是普通类(这意味着它们是 type 的实例,但你很少需要考虑这个)。你所要做的是使用父类的信息来创建子类,而不是通过实例化父类来创建子类。 - abarnert
显示剩余4条评论
3个回答

4
你在找这里要做的事情与实例化无关。那个词的意思是创建一个类的实例。你不能"从父类实例化子类",因为一个类对象通常不是其基类的实例(除了非常特殊的情况)。
你要做的是消除一些样板文件。
Python非常灵活 - 实例可以在任何地方定义属性,类可以对像__init____repr__这样的方法有任何签名,等等。这非常强大,但也意味着当你有一个非常重复的类层次时,你必须经常重复自己。但是 - 因为Python非常灵活 - 你可以编写工具来生成所有那些重复的东西,或者你可以使用语言自带的工具,比如 @dataclass1
from dataclasses import dataclass

@dataclass
class Parent:
    a: int
    b: int
    c: int
    d: int
    e: int
    f: int
    g: int
    h: int

@dataclass
class Child1(Parent):
    i: int
    j: int
    k: int

@dataclass
class Child2(Parent):
    x: int
    y: int
    z: int

只需要这样就可以定义你的类,包括自动生成__init__方法,可以处理位置参数和关键字参数,并将正确的参数传递给基类,以及__repr__方法,以某种有用的方式显示内容,静态类型提示可以由Mypy检查,等等,没有任何重复:

>>> c1 = Child1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
>>> c1
Child1(a=1, b=2, c=3, d=4, e=5, f=6, g=7, h=8, i=9, j=10, k=11)
>>> c1b = Child1(1, 2, 3, 4, 5, 6, 7, k=11, j=10, h=8, i=9)
>>> c1 == c1b
True
>>> c2 = Child2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
>>> c1 == c2
False

如果你阅读文档,你会发现那里有更多的灵活性(甚至在attrs中有更多),但默认设置80%的时间可以满足你的需求。

@dataclass 是在Python 3.7中添加的。您可以使用 pip install dataclasses 安装 3.6的兼容版本,但如果您需要使用3.5或2.7,则需要像 attrs 这样的第三方库。对于非常简单的情况,还可以查看 namedtuple,它适用于2.6和3.1及更高版本。


3

从您的例子中并不清楚是否适合使用继承。子类是否满足is a关系,即子类是否真的是父类的子类?如果不是,则应考虑使用组合(composition)而不是继承。

我建议使用组合,因为您在评论中提到了实例化父类并在“子”类中使用该实例。因此,您可以创建一个父类实例并将其传递给“子”类的__init__()方法,从而使用组合。

class Parent():
   def __init__(self, a, b, c, d, e, f, g):
       self.a = a
       self.b = b
       ...
       self.g = g

class Child1:
    def __init__(self, parent, h, i, j, k):
        self.parent = parent
        self.h = h
        self.i = i
        self.j = j
        self.k = k

在子类中,您可以通过 self.parent.a 访问父类属性。

0

你可以将所有参数作为列表传递给子类的 __init__,将所需数量传递给超类,并在子类本身中使用其余参数:

class Parent():
    def __init__(self,a,b,c):
        print(a,b,c)

class Child1(Parent):
    def __init__(self, *args):
        super().__init__(*args[:3]) # 3 for the parent
        self.x, self.y = args[3:] # 2 for myself
        print(self.x, self.y)

Child1(1,2,3,4,5)
#1 2 3
#4 5
#<__main__.Child1 object at 0x7f5d70c3c9e8>

出于项目特定原因,我更喜欢不使用 *args。在Python中似乎无法从父类实例化子类。 - MoneyBall
@MoneyBall 你在问“如果不使用 *args,如何做与 *args 相同的事情?” 如果是这样,那是不可能的,但可以做类似的事情。例如,如果你想要一个函数接收固定数量的参数,但这个固定数量是在创建函数时由变量指定的,你可以编写一个创建该函数的函数。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接