Python 超级方法和调用替代方法

发布于 2024-10-18 05:18:54 字数 177 浏览 1 评论 0原文

我到处都看到应该通过以下方式调用超类方法的示例:

super(SuperClass, instance).method(args)

这样做有什么缺点吗:

SuperClass.method(instance, args)

I see everywhere examples that super-class methods should be called by:

super(SuperClass, instance).method(args)

Is there any disadvantage to doing:

SuperClass.method(instance, args)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(2

梦归所梦 2024-10-25 05:18:54

考虑以下情况:

class A(object):
    def __init__(self):
        print('Running A.__init__')
        super(A,self).__init__()
class B(A):
    def __init__(self):
        print('Running B.__init__')        
        # super(B,self).__init__()
        A.__init__(self) 

class C(A):
    def __init__(self):
        print('Running C.__init__')
        super(C,self).__init__()
class D(B,C):
    def __init__(self):
        print('Running D.__init__')
        super(D,self).__init__()

foo=D()

因此,这些类形成了所谓的继承菱形:

    A
   / \
  B   C
   \ /
    D

运行代码会产生结果

Running D.__init__
Running B.__init__
Running A.__init__

这很糟糕,因为跳过了 C__init__。原因是 B__init__ 直接调用 A__init__

super的目的是解决继承钻石。如果您取消注释

# super(B,self).__init__()

并注释掉

A.__init__(self) 

代码,则会产生更理想的结果:

Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__

现在所有 __init__ 方法都会被调用。请注意,在定义 B.__init__ 时,您可能会认为 super(B,self).__init__() 与调用相同A.__init__(self),但你错了。在上述情况下,super(B,self).__init__()实际上调用了C.__init__(self)

天哪,BC 一无所知,但 super(B,self) 知道调用 C' s __init__?原因是因为 self.__class__.mro() 包含 C。换句话说,self(或者上面的foo)了解C

所以要小心——两者是不可替代的。它们可以产生截然不同的结果。

使用 super 有陷阱。 所有的组件之间需要相当程度的协调。继承图中的类。 (例如,它们必须具有相同的 __init__ 调用签名,因为任何特定的 __init__ 都不知道还有哪个 __init__ super 可能会调用 next,或者
否则使用**kwargs .) 此外,您必须在任何地方都使用 super 保持一致。跳过一次(如上面的示例),您就违背了 super 的全部目的。
请参阅链接了解更多陷阱。

如果您可以完全控制类层次结构,或者避免继承菱形,那么就不需要 super

Consider the following situation:

class A(object):
    def __init__(self):
        print('Running A.__init__')
        super(A,self).__init__()
class B(A):
    def __init__(self):
        print('Running B.__init__')        
        # super(B,self).__init__()
        A.__init__(self) 

class C(A):
    def __init__(self):
        print('Running C.__init__')
        super(C,self).__init__()
class D(B,C):
    def __init__(self):
        print('Running D.__init__')
        super(D,self).__init__()

foo=D()

So the classes form a so-called inheritance diamond:

    A
   / \
  B   C
   \ /
    D

Running the code yields

Running D.__init__
Running B.__init__
Running A.__init__

That's bad because C's __init__ is skipped. The reason for that is because B's __init__ calls A's __init__ directly.

The purpose of super is to resolve inheritance diamonds. If you un-comment

# super(B,self).__init__()

and comment-out

A.__init__(self) 

the code yields the more desireable result:

Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__

Now all the __init__ methods get called. Notice that at the time you define B.__init__ you might think that super(B,self).__init__() is the same as calling A.__init__(self), but you'd be wrong. In the above situation, super(B,self).__init__() actually calls C.__init__(self).

Holy smokes, B knows nothing about C, and yet super(B,self) knows to call C's __init__? The reason is because self.__class__.mro() contains C. In other words, self (or in the above, foo) knows about C.

So be careful -- the two are not fungible. They can yield vastly different results.

Using super has pitfalls. It takes a considerable level of coordination between all the classes in the inheritance diagram. (They must, for example, either have the same call signature for __init__, since any particular __init__ would not know which other __init__ super might call next, or
else use **kwargs.) Furthermore, you must be consistent about using super everywhere. Skip it once (as in the above example) and you defeat the entire purpose of super.
See the link for more pitfalls.

If you have full control over your class hierarchy, or you avoid inheritance diamonds, then there is no need for super.

靑春怀旧 2024-10-25 05:18:54

尽管你的例子有些误导,但没有任何惩罚。在第一个示例中,它应该是

super(SubClass, instance).method(args)  # Sub, not SuperClass

,这导致我引用 Python 文档

super 有两种典型的用例。在单继承的类层次结构中,可以使用super来引用父类,而无需显式命名它们,从而使代码更易于维护。这种用法与其他编程语言中 super 的使用非常相似。

第二个用例是支持动态执行环境中的协作多重继承。此用例是 Python 所独有的,在静态编译语言或仅支持单继承的语言中找不到。这使得实现“菱形图”成为可能,其中多个基类实现相同的方法。良好的设计要求该方法在每种情况下都具有相同的调用签名(因为调用顺序是在运行时确定的,因为该顺序适应类层次结构中的变化,并且因为该顺序可以包括在运行时之前未知的同级类)。

基本上,通过使用第一种方法,您不必在单类层次结构中对父类进行硬编码,并且在使用多个方法时,您根本无法真正使用第二种方法(有效地/有效地)执行您想要的操作遗产。

There's no penalty as-is, though your example is somewhat misguided. In the first example, it should be

super(SubClass, instance).method(args)  # Sub, not SuperClass

and that leads me to quote the Python docs:

There are two typical use cases for super. In a class hierarchy with single inheritance, super can be used to refer to parent classes without naming them explicitly, thus making the code more maintainable. This use closely parallels the use of super in other programming languages.

The second use case is to support cooperative multiple inheritance in a dynamic execution environment. This use case is unique to Python and is not found in statically compiled languages or languages that only support single inheritance. This makes it possible to implement “diamond diagrams” where multiple base classes implement the same method. Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

Basically, by using the first method you don't have to hard-code your parent class in there for single-class hierarchies, and you simply can't really do what you want (efficiently/effectively) using the second method when using multiple inheritance.

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