Python派生类和基类的属性? [英] Python derived class and base class attributes?
问题描述
似乎没有很好的在线文档:
如果我做一个派生类,它会自动拥有基类的所有属性吗?但是什么是 BaseClass .__ init()
for,你还需要做到其他基类方法吗? BaseClass .__ init __()
需要参数吗?如果你有你的基类的参数 __ init __()
,它们也被派生类使用,你需要显式设置参数到派生classe的 __ init __()
,或者将它们设置为 BaseClass .__ init __()
如果在从BaseClass派生的类中实现 __ init __
,那么它将覆盖继承的 __ init __
方法,因此 BaseClass .__ init __
永远不会被调用。如果你需要为BaseClass调用 __ init __
方法(通常是这种情况),那么它由你来完成,并且通过调用 BaseClass .__ init __
,通常来自新实现的 __ init __
方法。
class Foo(object):
def __init __(self):
self.a = 10
def do_something $ b print self.a
class Bar(Foo):
def __init __(self):
self.b = 20
bar = ()
bar.do_something()
这将导致以下错误:
AttributeError:'Bar'对象没有属性'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
的实例c $ c> self )。
通常,当你在一个类的实例上调用一个方法时,类实例会自动传递第一个参数。类的实例上的方法称为 bound方法。 bar.do_something
是一个绑定方法的例子(你会注意到它被调用没有任何参数)。 Foo .__ init __
是未绑定的方法,因为它未附加到 Foo
,因此第一个参数, Foo
的实例需要明确传递。
我们将 self
传递给 Foo .__ init __
,这是 Bar
传递给 Bar
中的 __ init __
方法。由于 Bar
继承自 Foo
, Bar
的实例实例 Foo
,因此将 self
传递给 Foo .__ init __
是允许的。
很可能是你继承的类需要或接受更多的参数而不仅仅是类的一个实例。这些处理方式与您在 __ init __
中调用的任何方法一样:
class Foo(object):
def __init __(self,a = 10):
self.a = a
def do_something $ b print self.a
class Bar(Foo):
def __init __(self):
Foo .__ init __(self,20)
bar = Bar()
bar.do_something()
c> 20 。
如果你想实现一个接口,通过继承类完全暴露基类的所有初始化参数,你需要明确这样做。这通常使用* args和** kwargs参数(名称按照惯例)完成,它们是未显式命名的所有其余参数的占位符。下面的例子使用了我讨论过的所有内容:
class Foo(object):
def __init __ ,a,b = 10):
self.num = a * b
def do_something(self):
print self.num
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 __
。
这个例子也说明如果需要的话,任何覆盖的方法需要被显式地调用(如
最后一点,你会经常看到 super(ChildClass,self) .method()
(其中 ChildClass
是一些任意的子类),而不是调用 BaseClass
方法。讨论
super
是一个完整的其他问题,但足以说,在这些情况下,它通常被用来做完全正在做的是通过调用 BaseClass.method(self)
。简单来说, super
将方法调用委托给方法解析顺序中的下一个类--MRO(在单继承中是父类)。有关详情,请参见超级文档。
There seems to be no good online documentation on this:
If I make a derived class, will it automatically have all the attributes of the base class? But what's the BaseClass.__init()
for, do you also need to do it to other base class methods? Does BaseClass.__init__()
need arguments? If you have arguments for your base class __init__()
, are they also used by the derived class, do you need to explicitly set the arguments to the derived classe's __init__()
, or set them to BaseClass.__init__()
instead?
If you implement __init__
in a class derived from BaseClass, then it will overwrite the inherited __init__
method and so BaseClass.__init__
will never be called. If you need to call the __init__
method for BaseClass (as is normally the case), then its up to you to do that, and its done explicitly by calling BaseClass.__init__
, normally from within the newly implemented __init__
method.
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()
This will cause the following error:
AttributeError: 'Bar' object has no attribute 'a'
So, the do_something
method has been inherited as expected, but that method requires the attribute a
to have been set, which it never is because __init__
was also overwritten. We get round this by explicitly calling Foo.__init__
from within 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()
which prints 10
as expected. Foo.__init__
in this case expects a single argument which is an instance of Foo
(which by convention is called self
).
Normally, when you call a method on an instance of a class, the class instance is passed automatically as the first argument. Methods on an instance of a class are called bound methods. bar.do_something
is an example of a bound method (and you'll note that it is called without any arguments). Foo.__init__
is an unbound method because it is not attached to a particular instance of Foo
, so the first argument, an instance of Foo
, needs to be passed explicitly.
In our case, we pass self
to Foo.__init__
, which is the instance of Bar
that was passed to the __init__
method in Bar
. Since Bar
inherits from Foo
, instances of Bar
are also instances of Foo
, so passing self
to Foo.__init__
is allowed.
It is likely be the case that the class you are inheriting from requires or accepts more arguments than just an instance of the class. These are dealt with as you would with any method you're calling from within __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()
which would print 20
.
If you're trying to implement a interface that fully exposes all the initialisation arguments of the base class through your inheriting class, you'll need to do so explicitly. This is typically done with the *args and **kwargs arguments (the names are by convention), which are placeholders for all rest of the arguments that aren't explicitly named. The following example makes use of everything I've discussed:
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()
In this case, the argument c
is set to be 40, as it's the first argument to Bar.__init__
. The second argument is then incorporated into the variables args
and kwargs
(the * and ** is specific syntax that says expand the list/tuple or dictionary into separate arguments when passing to a function/method), and is passed on to Foo.__init__
.
This example also makes the point that any overwritten method needs to be called explicitly if that is what is required (as do_something
is in this case).
One final point, you will often see super(ChildClass, self).method()
(where ChildClass
is some arbitrary child class) being used instead of a call to the BaseClass
method explicitly. Discussion of super
is a whole other question, but suffice it to say, in these cases it's typically being used to do exactly what is being done by calling BaseClass.method(self)
. Briefly, super
delegates the method call to the next class in the method resolution order - the MRO (which in single inheritance is the parent class). See the documentation on super for more info.
这篇关于Python派生类和基类的属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!