26

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

4

2 回答 2

62

如果您__init__在从 BaseClass 派生的类中实现,那么它将覆盖继承的__init__方法,因此BaseClass.__init__永远不会被调用。如果您需要调用__init__BaseClass 的方法(通常是这种情况),那么由您来执行此操作,并通过调用显式完成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__也被覆盖了。我们通过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__in 方法的实例Bar。由于Bar继承自Foo, 的实例Bar也是 的实例,因此允许Foo传递selfto 。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(在单继承中是父类)。有关更多信息,请参阅super 上的文档。

于 2011-06-18T14:58:55.570 回答
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在调用 base 时通过__init__。这里没有火箭科学。

如果您的基类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-06-18T14:02:16.137 回答