似乎没有关于此的良好在线文档:
如果我创建一个派生类,它会自动拥有基类的所有属性吗?但是BaseClass.__init()
是什么,你还需要对其他基类方法做吗? BaseClass.__init__()
需要参数吗?如果您的基类__init__()
有参数,它们是否也被派生类使用,您是否需要显式设置派生classe __init__()
的参数,或将它们设置为BaseClass.__init__()
代替?
答案 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
传递给self
,Foo.__init__
是Bar
传递给__init__
Bar
方法的实例。由于Bar
继承自Foo
,Bar
的实例也是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__
的第一个参数。然后将第二个参数合并到变量args
和kwargs
中(*和**是特定语法,表示在传递给函数/方法时将列表/元组或字典扩展为单独的参数),并传递给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
并将未修改的参数传递给基类,或者复制基类签名,或者从其他地方提供参数,取决于您要完成的任务。