Python派生类和基类的属性? [英] Python derived class and base class attributes?

查看:315
本文介绍了Python派生类和基类的属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎没有很好的在线文档:
如果我做一个派生类,它会自动拥有基类的所有属性吗?但是什么是 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆