派生类是否自动具有基类的所有属性?

发布于 2024-11-15 23:18:37 字数 280 浏览 3 评论 0原文

似乎没有关于此的好的在线文档: 如果我创建一个派生类,它会自动拥有基类的所有属性吗?但是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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夜司空 2024-11-22 23:18:37

如果您在从 BaseClass 派生的类中实现 __init__,那么它将覆盖继承的 __init__ 方法,因此永远不会调用 BaseClass.__init__。如果您需要调用 BaseClass 的 __init__ 方法(​​通常是这种情况),那么由您来执行此操作,并且通过调用 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,但事实并非如此,因为< code>__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 的实例(按照惯例称为 self)。

通常,当您在类的实例上调用方法时,类实例会自动作为第一个参数传递。类实例上的方法称为绑定方法。 bar.do_something 是绑定方法的示例(您会注意到它的调用不带任何参数)。 Foo.__init__ 是一个未绑定方法,因为它没有附加到 Foo 的特定实例,因此第一个参数是 的实例>Foo,需要显式传递。

在我们的例子中,我们将 self 传递给 Foo.__init__,它是传递给 __init__Bar 的实例。 Bar 中的 code> 方法。由于 Bar 继承自 Foo,因此 Bar 的实例也是 Foo 的实例,因此传递 self< /code> 到 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 是某个任意子类),而不是调用 < 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 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.

梦行七里 2024-11-22 23:18:37

如果我创建一个派生类,它会自动拥有基类的所有属性吗?

类属性,是的。实例属性,否(仅仅因为它们在创建类时不存在),除非派生类中没有 __init__ ,在这种情况下,将调用基类,并设置实例属性。

BaseClass.init() 需要参数吗?

取决于类及其 __init__ 签名。如果您在派生类中显式调用 Base.__init__,则至少需要传递 self 作为第一个参数。如果有

class Base(object):
    def __init__(self):
        # something

,那么很明显 __init__ 不接受其他参数。如果你有

class Base(object):
    def __init__(self, argument):
        # something

,那么你必须在调用基__init__时传递argument。这里没有火箭科学。

如果你的基类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 并仅将未修改的参数传递给基类,还是复制基类签名,或从其他地方提供参数,都取决于您想要完成的任务。

If I make a derived class, will it automatically have all the attributes of the base class?

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.

Does BaseClass.init() need arguments?

Depends on the class and its __init__ signature. If you're explicitly calling Base.__init__ in the derived class, you will at least need to pass self as the first argument. If you have

class Base(object):
    def __init__(self):
        # something

then it's rather obvious that no other arguments are accepted by the __init__. If you'd have

class Base(object):
    def __init__(self, argument):
        # something

then you have to pass argument when calling base __init__. No rocket science in here.

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?

Again, if the derived class doesn't have __init__, base one will be used instead.

class Base(object):
    def __init__(self, foo):
        print 'Base'

class Derived(Base):
    pass

Derived()   # TypeError
Derived(42) # prints Base

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文