python类层次结构中的初始化顺序

2024-03-29 00:05:26 发布

您现在位置:Python中文网/ 问答频道 /正文

C++中,给定一个类层次结构,最派生类的Cor调用其基类Cor,然后在实例化派生的部分之前初始化对象的基部。在Python中,我想了解在我有这样一个需求的情况下发生了什么,即派生子类一个给定的基类,该基类在它的__init__方法中接受一个可调用的,然后它会调用该方法。可调用的特性是我在派生类的__init__中传递的一些参数,这也是我定义可调用函数的地方。然后,我的想法是在定义了__call__操作符之后,将派生类本身传递给它的基类

class Derived(Base):
    def __init__(self, a, b):
        def _process(c, d):
            do_something with a and b

        self.__class__.__call__ = _process
        super(Derived, self).__init__(self)
  1. 这是处理这个问题的一种恶作剧的方法吗
  2. 这里初始化的确切顺序是什么?是否需要调用super作为__init__方法中的第一条指令,还是可以按我的方式进行调用
  3. 我不知道在python中使用带参数还是不带参数的super是否被认为是好的做法>;第3.6条

Tags: 方法self参数定义层次结构initdefcall
1条回答
网友
1楼 · 发布于 2024-03-29 00:05:26

What is the exact order of initialization here?

很明显,您在代码中看到的-Base.__init__()只有在您显式地请求它时才被调用(使用super()调用)。如果Base也有父级,并且链中的每个人都使用super()调用,则将根据mro调用父级初始值设定项

基本上,Python是一种“运行时语言”——除了字节码编译阶段,所有的事情都发生在运行时——所以很少有“黑魔法”在发生(实际上,它的大部分内容都是文档化的,并且完全公开给那些想深入研究或进行元编程的人)

Does one needs to call super as a first instruction in the init method or is it ok to do it the way I did?

在您认为适合具体用例的地方调用父级的方法—您只需注意在定义实例属性之前不要使用实例属性(直接或不太明显—通过依赖于这些属性的方法调用间接)

I am confused whether it is considered good practice to use super with or without arguments in python > 3.6

如果您不需要向后兼容,请使用super()而不带参数-除非您想显式跳过MRO中的某个类,但是很可能您的设计中有一些值得商榷的地方(但是,有时我们不能仅仅为了避免一个非常特殊的情况而重写整个代码库,所以只要你明白自己在做什么和为什么,那也没关系

现在谈谈你的核心问题:

class Derived(Base):
    def __init__(self, a, b):
        def _process(c, d):
            do_something with a and b

        self.__class__.__call__ = _process
        super(Derived, self).__init__(self)

self.__class__.__call__是类属性,由类的所有实例共享。这意味着您必须确保您只使用了类的一个实例(这似乎不是这里的目标),或者准备好获得完全随机的结果,因为每个新实例都将用它自己的版本覆盖self.__class__.__call__

如果您想让每个实例的__call__方法调用它自己的process()版本,那么有一个更简单的解决方案-只需将_process作为实例属性并从__call__调用它:

class Derived(Base):
    def __init__(self, a, b):
        def _process(c, d):
            do_something with a and b

        self._process = _process
        super(Derived, self).__init__(self)

   def __call__(self, c, d):
       return self._process(c, d)

或者更简单:

class Derived(Base):
    def __init__(self, a, b):
        super(Derived, self).__init__(self)
        self._a = a
        self._b = b

   def __call__(self, c, d):
       do_something_with(self._a, self._b)

编辑:

Base requires a callable in ins init method.

如果您的示例片段更接近您的实际用例,这会更好

But when I call super().init() the call method of Derived should not have been instantiated yet or has it?

这是个好问题。。。实际上,Python方法并不是你想象的那样。使用def语句在class语句体中定义的仍然是普通函数,如您自己所见:

class Foo: ... def bar(self): pass ... Foo.bar

“方法”仅在属性查找解析为恰好是函数的类属性时才实例化:

Foo().bar main.Foo object at 0x7f3cef4de908>> Foo().bar main.Foo object at 0x7f3cef4de940>>

(如果你想知道这是怎么发生的,那就是documented here

实际上,它们只是围绕函数、实例和类(或classmethods的函数和类)的精简包装器,将调用委托给底层函数,将实例(或类)作为第一个参数注入。在CS术语中,Python方法是函数对实例(或类)的部分应用

正如我前面提到的,Python是一种运行时语言,defclass都是可执行语句。因此,在定义Derived类时,创建Base类对象的class语句已经执行(否则Base根本不存在),所有的class语句块都首先执行(以定义函数和其他类属性)

因此,“当您调用super().__init()__时,__call__函数Base已经实例化(假设它是在class语句中为Base定义的,但这是目前为止最常见的情况)

相关问题 更多 >