【问题标题】:Does a derived class automatically have all the attributes of the base class?派生类是否自动具有基类的所有属性?
【发布时间】:2011-09-17 19:05:13
【问题描述】:

似乎没有关于此的好的在线文档: 如果我做一个派生类,它会自动拥有基类的所有属性吗?但是BaseClass.__init() 是干什么用的,你还需要对其他基类方法这样做吗? BaseClass.__init__() 需要参数吗?如果您的基类__init__() 有参数,派生类是否也使用它们,您是否需要将参数显式设置为派生类的__init__(),或者将它们设置为BaseClass.__init__()

【问题讨论】:

    标签: python class inheritance attributes derived


    【解决方案1】:

    如果您在派生自 BaseClass 的类中实现 __init__,那么它将覆盖继承的 __init__ 方法,因此永远不会调用 BaseClass.__init__。如果您需要为 BaseClass 调用 __init__ 方法(通常是这种情况),那么由您来执行此操作,并通过调用 BaseClass.__init__ 显式完成,通常从新实现的 __init__ 方法中完成。

    class Foo(object):
        def __init__(self):
            self.a = 10
    
        def do_something(self):
            print self.a
    
    class Bar(Foo):
        def __init__(self):
            self.b = 20
    
    bar = Bar()
    bar.do_something()
    

    这将导致以下错误:

    AttributeError: 'Bar' object has no attribute 'a'
    

    因此,do_something 方法已按预期继承,但该方法需要设置属性 a,但它从来没有设置,因为 __init__ 也被覆盖了。我们通过在Bar.__init__ 中显式调用Foo.__init__ 来解决这个问题。

    class Foo(object):
        def __init__(self):
            self.a = 10
    
        def do_something(self):
            print self.a
    
    class Bar(Foo):
        def __init__(self):
            Foo.__init__(self)
            self.b = 20
    
    bar = Bar()
    bar.do_something()
    

    按预期打印10。在这种情况下,Foo.__init__ 需要一个单独的参数,它是 Foo 的一个实例(按照惯例称为 self)。

    通常,当您在类的实例上调用方法时,类实例会自动作为第一个参数传递。类实例上的方法称为绑定方法bar.do_something 是绑定方法的一个示例(您会注意到它是在没有任何参数的情况下调用的)。 Foo.__init__ 是一个未绑定的方法,因为它没有附加到Foo 的特定实例,所以第一个参数,Foo 的实例,需要显式传递。

    在我们的例子中,我们将self 传递给Foo.__init__,这是传递给Bar__init__ 方法的Bar 的实例。由于Bar 继承自FooBar 的实例也是Foo 的实例,因此允许将self 传递给Foo.__init__

    您所继承的类很可能需要或接受更多参数,而不仅仅是类的实例。这些处理方式与您在 __init__ 中调用的任何方法一样:

    class Foo(object):
        def __init__(self, a=10):
            self.a = a
    
        def do_something(self):
            print self.a
    
    class Bar(Foo):
        def __init__(self):
            Foo.__init__(self, 20)
    
    bar = Bar()
    bar.do_something()
    

    这将打印20

    如果您尝试实现一个接口,该接口通过您的继承类完全公开基类的所有初始化参数,您需要明确地这样做。这通常使用 *args 和 **kwargs 参数(名称按约定)完成,它们是所有未明确命名的其余参数的占位符。以下示例利用了我讨论过的所有内容:

    class Foo(object):
        def __init__(self, a, b=10):
            self.num = a * b
    
        def do_something(self):
            print self.num
    
    class Bar(Foo):
        def __init__(self, c=20, *args, **kwargs):
            Foo.__init__(self, *args, **kwargs)
            self.c = c
    
        def do_something(self):
            Foo.do_something(self)
            print self.c
    
    
    bar = Bar(40, a=15)
    bar.do_something()
    

    在这种情况下,参数 c 设置为 40,因为它是 Bar.__init__ 的第一个参数。然后将第二个参数合并到变量 argskwargs 中(* 和 ** 是特定语法,表示在传递给函数/方法时将列表/元组或字典扩展为单独的参数),并被传递到Foo.__init__

    此示例还指出,如果需要,任何覆盖的方法都需要显式调用(如do_something在这种情况下)。

    最后一点,您经常会看到使用super(ChildClass, self).method()(其中ChildClass 是某个任意子类)而不是显式调用BaseClass 方法。对super 的讨论完全是另一个问题,但我只想说,在这些情况下,它通常用于通过调用BaseClass.method(self) 来执行正在执行的操作。简而言之,super 将方法调用委托给方法解析顺序中的下一个类 - MRO(在单继承中是父类)。请参阅documentation on super 了解更多信息。

    【讨论】:

    • @PM2Ring 你是什么意思?
    • @PM2Ring 不,这就是重点。 Bar 使用 c 属性扩展 FooFoo 不能使用关键字 c。除非我错过了什么?
    • 抱歉,我好像有点糊涂了(我无法在手机上轻松测试)。
    【解决方案2】:

    如果我创建一个派生类,它会自动拥有基类的所有属性吗?

    类属性,是的。实例属性,没有(只是因为在创建类时它们不存在),除非派生类中没有__init__,在这种情况下将调用基类,并设置实例属性。

    BaseClass.init() 是否需要参数?

    取决于类及其__init__ 签名。如果您在派生类中显式调用Base.__init__,则至少需要将self 作为第一个参数传递。如果你有

    class Base(object):
        def __init__(self):
            # something
    

    那么很明显__init__ 不接受其他参数。如果你有

    class Base(object):
        def __init__(self, argument):
            # something
    

    那么你必须在调用 base __init__ 时传递 argument。这里没有火箭科学。

    如果您的基类 init() 有参数,派生类是否也使用它们,您是否需要将参数显式设置为派生类的 init(),还是将它们设置为 BaseClass.init()?

    同样,如果派生类没有__init__,则将使用基类。

    class Base(object):
        def __init__(self, foo):
            print 'Base'
    
    class Derived(Base):
        pass
    
    Derived()   # TypeError
    Derived(42) # prints Base
    

    在其他情况下,您需要以某种方式处理它。无论您是使用*args, **kwargs 并将未经修改的参数传递给基类,还是复制基类签名或从其他地方提供参数,都取决于您要完成的任务。

    【讨论】:

      猜你喜欢
      • 2011-11-04
      • 1970-01-01
      • 1970-01-01
      • 2016-09-20
      • 1970-01-01
      • 1970-01-01
      • 2014-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多