似乎没有很好的在线文档:如果我做一个派生类,它会自动具有基类的所有属性吗?但是有什么BaseClass.__init()
用,你还需要对其他基类方法这样做吗?BaseClass.__init__()
需要论据吗?如果您的基类有参数,__init__()
派生类是否也使用它们,您是否需要将参数显式设置为派生类的__init__()
,或者将它们设置为BaseClass.__init__()
?
2 回答
如果您__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
,需要显式传递。
在我们的例子中,我们传递self
给Foo.__init__
,这是Bar
传递给__init__
in 方法的实例Bar
。由于Bar
继承自Foo
, 的实例Bar
也是 的实例,因此允许Foo
传递self
to 。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__
. 然后将第二个参数合并到变量args
中kwargs
(* 和 ** 是特定语法,表示在传递给函数/方法时将列表/元组或字典扩展为单独的参数),并传递给Foo.__init__
.
此示例还指出,如果需要,任何被覆盖的方法都需要显式调用(如do_something
本例所示)。
最后一点,您经常会看到super(ChildClass, self).method()
(在哪里ChildClass
是一些任意子类)被使用而不是显式调用该BaseClass
方法。讨论super
是另一个问题,但我只想说,在这些情况下,它通常用于通过调用BaseClass.method(self)
. 简而言之,super
将方法调用委托给方法解析顺序中的下一个类 - MRO(在单继承中是父类)。有关更多信息,请参阅super 上的文档。
如果我做一个派生类,它会自动拥有基类的所有属性吗?
类属性,是的。实例属性,没有(因为它们在创建类时不存在),除非__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
并将未经修改的参数传递给基类,还是复制基类签名,还是从其他地方提供参数,都取决于您要完成的工作。