为什么在这里发生递归? [英] Why a recursion happens here?

查看:161
本文介绍了为什么在这里发生递归?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近阅读了一篇有关如何在Python中制作单身人士的有趣讨论
其中一个解决方案是一个棘手的装饰器,在其代码中定义一个类来代替装饰类: / p>

  def singleton(class_):
class class_w(class_):
_instance = None
def __new __(class2,* args,** kwargs):
如果class_w._instance为None:
class_w._instance = super(class_w,class2).__ new __(class2,* args,** kwargs)
class_w._instance._sealed = False
return class_w._instance
def __init __(self,* args,** kwargs):
if self._sealed:
return
super(class_w,self).__ init __(* args,** kwargs)
self._sealed = True
class_w .__ name__ = class _.__ name__
return class_w

@singleton
class MyClass(object):
def __init __(self,text):
print text
@classmethod
def name(class_):
print class _.__ name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id id(y)

输出为:

  111#__init__仅在第一次调用
MyClass#保存__name__
True#这实际上是同一个实例

据说,如果我们使用 super(MyClass,self).__ init __(text) in in in ,我们进入递归



我测试了,确实发生了递归。
但是,如我所知, MyClass 继承对象,因此 super ,self)应该只是 object ,但结果是 super(MyClass,self) __ main __。MyClass



你能解释一下,为什么会发生递归?

解决方案

问题是通过写 super(MyClass,self).__ init__ () code>被调用。但是装饰器用自己的子类代替 MyClass 。因此,当你的原始 __ init __ 方法被调用 MyClass 实际上是指定义执行方法的类的子类。 / p>

一步一步说,我将调用原始类(如源代码中所写) OrigMyClass ,以及生成的版本(装饰器之后) DecMyClass 。我将使用 MyClass 作为一个变量,因为它的含义在执行过程中改变。


  1. OrigMyClass 上定义 __ init __ 方法,但 __init __ 方法调用 super(MyClass,self),而不是 super(OrigMyClass,self) 。因此,实际调用什么方法取决于 MyClass 在调用方法时指向。与任何其他变量一样,在执行时查找 MyClass 的值;将它放在 super 调用或 __ init __ 方法中并不会魔术地绑定到它碰巧发生的类当你写的时候;


  2. 装饰器运行时,函数中的变量将被调用,而不是当它们被定义时。装饰器定义一个新类 DecMyClass 作为 OrigMyClass 的子类。 DecMyClass 定义调用 super(DecMyClass,self) __ init __ >


  3. 装饰器运行后,名称 MyClass 绑定到类 DecMyClass 。注意,这意味着当 super(MyClass,self)调用稍后执行时,它将执行 super(DecMyClass,self)


  4. 当您执行 MyClass(111)时, c $ c> DecMyClass DecMyClass .__ init __ 调用 super(DecMyClass,self).__ init __ 。这将执行 OrigMyClass .__ init __


  5. OrigMyClass .__ init __ 调用 super(MyClass,self).__ init __ 。因为 MyClass 是指 DecMyClass ,这与 super(DecMyClass,self)相同。 __init __ 。但是 DecMyClass OrigMyClass 的子类。关键点是因为 MyClass 指的是 DecMyClass OrigMyClass


  6. 因此 super(DecMyClass,self)。 __init __ 再次调用 OrigMyClass .__ init __ ,再次调用自身,依此类推到无穷大。


效果与此代码相同,这可能会使执行路径更加明显:

 >>> class Super(object):
... def __init __(self):
... print在超级初始化
... super(Sub,self).__ init __ b $ b>>>> class Sub(Super):
... def __init __(self):
... printIn sub init
... super(Sub,self).__ init __()

注意 Super 调用 super(Sub,self)。它试图调用超类方法,但它试图调用 Sub 的超类方法。 Sub 的超类为 Super ,因此 Super



编辑:为了澄清您提出的名称查找问题,下面是另一个略有不同的示例,具有相同的结果:

 >>> class Super(object):
... def __init __(self):
... print在超级初始化
... super(someClass,self).__ init __ b $ b>>>> class Sub(Super):
... def __init __(self):
... printIn sub init
... super(Sub,self).__ init __()
>>>> someClass = Sub

这应该清楚, super (第一个参数, someClass )在任何方面都不是特殊的。它只是一个普通的名字,其值在普通的时候,即在执行 super 调用时以普通方式查找。如此示例所示,在定义方法时,变量甚至不必存在;在您调用方法时查找该值。


Recently I read an interesting discussion on how to make a singleton in Python. One of the solutions was a tricky decorator defining a class inside its code as a substitute for decorated class:

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

Output is:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

It is stated, that if we use super(MyClass, self).__init__(text) inside __init__ of MyClass, we get into recursion.

I tested and indeed the recursion happens. But, as I understand, MyClass inherits object, so super(MyClass, self) should just merely be object, but it turns out that super(MyClass, self) is __main__.MyClass

Could you explain what happens here step by step for me to understand the reasons why the recursion happens?

解决方案

The problem is that by writing super(MyClass, self).__init__(text), you are saying to use the super relative to whatever class MyClass refers to at the time super is called. But the decorator replaces MyClass with a subclass of itself. So when your original __init__ method is called MyClass actually refers to a subclass of the class which defines the executing method.

To say it step by step, I'm going to call the original class (as written in the source) OrigMyClass, and the resulting version (after the decorator) DecMyClass. I'll use MyClass just as a variable, because its meaning changes during the course of execution.

  1. You define an __init__ method on OrigMyClass, but that __init__ method calls super(MyClass, self), not super(OrigMyClass, self). Thus, what method will actually be called depends on what MyClass refers to at the time the method is called. The value of MyClass is looked up at execution time like any other variable; placing it inside the super call or inside the __init__ method does not magically bind it to the class it happens to be in when you write it; variables in functions are evaluated when they are called, not when they are defined.

  2. The decorator runs. The decorator defines a new class DecMyClass as a subclass of OrigMyClass. DecMyClass defines an __init__ that calls super(DecMyClass, self).

  3. After the decorator runs, the name MyClass is bound to the class DecMyClass. Note that this means that when the super(MyClass, self) call later executes, it will be doing super(DecMyClass, self).

  4. When you do MyClass(111), you instantiate an object of DecMyClass. DecMyClass.__init__ calls super(DecMyClass, self).__init__. This executes OrigMyClass.__init__.

  5. OrigMyClass.__init__ calls super(MyClass, self).__init__. Because MyClass refers to DecMyClass, this is the same as super(DecMyClass, self).__init__. But DecMyClass is a subclass of OrigMyClass. The key point is that because MyClass refers to DecMyClass, OrigMyClass is actually calling super on a subclass of itself.

  6. Thus super(DecMyClass, self).__init__ again calls OrigMyClass.__init__, which again calls itself, and so on to infinity.

The effect is the same as this code, which may make the execution path more obvious:

>>> class Super(object):
...     def __init__(self):
...         print "In super init"
...         super(Sub, self).__init__()
>>> class Sub(Super):
...     def __init__(self):
...         print "In sub init"
...         super(Sub, self).__init__()

Note that Super calls super(Sub, self). It is trying to call a superclass method, but it tries to call the superclass method of Sub. The superclass of Sub is Super, so Super winds up calling its own method again.

Edit: Just to clarify the name-lookup issues you raised, here's another slightly different example that has the same result:

>>> class Super(object):
...     def __init__(self):
...         print "In super init"
...         super(someClass, self).__init__()
>>> class Sub(Super):
...     def __init__(self):
...         print "In sub init"
...         super(Sub, self).__init__()
>>> someClass = Sub

This should make it clear that the class argument to super (the first argument, here someClass) is not special in any way. It is just an ordinary name whose value is looked up in the ordinary way at the ordinary time, namely when the super call is executed. As shown by this example, the variable doesn't even have to exist at the time you define the method; the value is looked up at the time you call the method.

这篇关于为什么在这里发生递归?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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