派生类是否自动拥有基类的所有属性?

时间:2011-06-18 13:45:02

标签: python class inheritance attributes derived

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

2 个答案:

答案 0 :(得分:55)

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

__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__内明确调用Foo.__init__来解决此问题。

Bar.__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传递给selfFoo.__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

答案 1 :(得分:11)

  

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

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

  

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

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

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

然后很明显,__init__没有接受其他论点。如果你有

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

然后你必须在调用基础argument时传递__init__。这里没有火箭科学。

  

如果您的基类 init ()有参数,它们是否也被派生类使用,您是否需要显式设置派生classe的 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并将未修改的参数传递给基类,或者复制基类签名,或者从其他地方提供参数,取决于您要完成的任务。