派生类是否自动具有基类的所有属性?
似乎没有关于此的好的在线文档: 如果我创建一个派生类,它会自动拥有基类的所有属性吗?但是BaseClass.__init()
是做什么用的,是否还需要对其他基类方法执行此操作? BaseClass.__init__()
需要参数吗?如果您的基类 __init__()
有参数,派生类是否也使用它们,是否需要将参数显式设置为派生类的 __init__()
,或者将它们设置为 BaseClass.__init__()
?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您在从 BaseClass 派生的类中实现 __init__,那么它将覆盖继承的 __init__ 方法,因此永远不会调用
BaseClass.__init__
。如果您需要调用 BaseClass 的 __init__ 方法(通常是这种情况),那么由您来执行此操作,并且通过调用 BaseClass.__init__ 显式完成。 ,通常来自新实现的 __init__ 方法。这将导致以下错误:
因此,
do_something
方法已按预期继承,但该方法需要设置属性a
,但事实并非如此,因为< code>__init__ 也被覆盖。我们通过从Bar.__init__
中显式调用Foo.__init__
来解决这个问题。按预期打印
10
。在这种情况下,Foo.__init__
需要一个参数,它是Foo
的实例(按照惯例称为self
)。通常,当您在类的实例上调用方法时,类实例会自动作为第一个参数传递。类实例上的方法称为绑定方法。
bar.do_something
是绑定方法的示例(您会注意到它的调用不带任何参数)。Foo.__init__
是一个未绑定方法,因为它没有附加到Foo
的特定实例,因此第一个参数是的实例>Foo
,需要显式传递。在我们的例子中,我们将
self
传递给Foo.__init__
,它是传递给__init__
Bar 的实例。Bar
中的 code> 方法。由于Bar
继承自Foo
,因此Bar
的实例也是Foo
的实例,因此传递self< /code> 到
Foo.__init__
是允许的。您所继承的类很可能需要或接受更多的参数,而不仅仅是该类的实例。这些的处理方式与处理从
__init__
中调用的任何方法一样:它将打印
20
。如果您尝试实现一个通过继承类完全公开基类的所有初始化参数的接口,则需要显式地这样做。这通常是使用 *args 和 **kwargs 参数(名称按照约定)完成的,它们是所有其余未显式命名的参数的占位符。以下示例利用了我讨论过的所有内容:
在本例中,参数
c
设置为 40,因为它是Bar.__init__
的第一个参数。然后,第二个参数被合并到变量 args 和 kwargs 中(* 和 ** 是特定语法,表示在传递给时将列表/元组或字典扩展为单独的参数函数/方法),并传递给Foo.__init__
。此示例还表明,如果需要的话,任何 被覆盖的方法都需要显式调用(如本例中的
do_something
)。最后一点,您经常会看到使用
super(ChildClass, self).method()
(其中ChildClass
是某个任意子类),而不是调用 < code>BaseClass 显式方法。对 super 的讨论是一个完全不同的问题,但可以说,在这些情况下,它通常用于通过调用 BaseClass.method(self) 来完成正在做的事情。 >。简而言之,super
将方法调用委托给方法解析顺序中的下一个类 - MRO(在单继承中是父类)。有关详细信息,请参阅有关 super 的文档。If you implement
__init__
in a class derived from BaseClass, then it will overwrite the inherited__init__
method and soBaseClass.__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 callingBaseClass.__init__
, normally from within the newly implemented__init__
method.This will cause the following error:
So, the
do_something
method has been inherited as expected, but that method requires the attributea
to have been set, which it never is because__init__
was also overwritten. We get round this by explicitly callingFoo.__init__
from withinBar.__init__
.which prints
10
as expected.Foo.__init__
in this case expects a single argument which is an instance ofFoo
(which by convention is calledself
).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 ofFoo
, so the first argument, an instance ofFoo
, needs to be passed explicitly.In our case, we pass
self
toFoo.__init__
, which is the instance ofBar
that was passed to the__init__
method inBar
. SinceBar
inherits fromFoo
, instances ofBar
are also instances ofFoo
, so passingself
toFoo.__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__
: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:
In this case, the argument
c
is set to be 40, as it's the first argument toBar.__init__
. The second argument is then incorporated into the variablesargs
andkwargs
(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 toFoo.__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()
(whereChildClass
is some arbitrary child class) being used instead of a call to theBaseClass
method explicitly. Discussion ofsuper
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 callingBaseClass.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.类属性,是的。实例属性,否(仅仅因为它们在创建类时不存在),除非派生类中没有 __init__ ,在这种情况下,将调用基类,并设置实例属性。
取决于类及其
__init__
签名。如果您在派生类中显式调用Base.__init__
,则至少需要传递self
作为第一个参数。如果有,那么很明显
__init__
不接受其他参数。如果你有,那么你必须在调用基
__init__
时传递argument
。这里没有火箭科学。同样,如果派生类没有
__init__
,则将使用基类。在其他情况下,您需要以某种方式处理它。无论您使用 *args、**kwargs 并仅将未修改的参数传递给基类,还是复制基类签名,或从其他地方提供参数,都取决于您想要完成的任务。
Class attributes, yes. Instance attributes, no (simply because they don't exist when the class is created), unless there's no
__init__
in the derived class, in which case the base one will be called instead, and will set the instance attributes.Depends on the class and its
__init__
signature. If you're explicitly callingBase.__init__
in the derived class, you will at least need to passself
as the first argument. If you havethen it's rather obvious that no other arguments are accepted by the
__init__
. If you'd havethen you have to pass
argument
when calling base__init__
. No rocket science in here.Again, if the derived class doesn't have
__init__
, base one will be used instead.In other case, you need to take care of it somehow. Whether you use
*args, **kwargs
and just pass arguments unmodified to the base class, or copy the base class signature, or supply arguments from elsewhere, depends on what you're trying to accomplish.