简体   繁体   English

Python类装饰器扩展类导致递归

[英]Python class decorator extending class causes recursion

I'm overwriting the save method of a ModelForm and I don't know why it would cause recursion: 我正在覆盖ModelForm的save方法,我不知道它为什么会导致递归:

@parsleyfy
class AccountForm(forms.ModelForm):
    def save(self, *args, **kwargs):
        # some other code...
        return super(AccountForm, self).save(*args,**kwargs)

Causes this: 导致这个:

maximum recursion depth exceeded while calling a Python object

Stacktrace shows this line repetitively calling itself: Stacktrace显示此行反复调用自身:

return super(AccountForm, self).save(*args,**kwargs) 

Now, the parsley decorator is like this: 现在,欧芹装饰器是这样的:

def parsleyfy(klass):
    class ParsleyClass(klass):
      # some code here to add more stuff to the class
    return ParsleyClass

As @DanielRoseman suggested that the Parsley decorator extending the AccountForm causes the super(AccountForm,self) to keep calling itself, what's the solution? 正如@DanielRoseman所说,扩展AccountForm的Parsley装饰器导致super(AccountForm,self)继续调用自己,解决方案是什么?

Also I cannot get my head around this why this would cause recursion. 此外,我无法理解为什么这会导致递归。

What you could do is just call the parent's method directly: 你能做的就是直接调用父方法:

@parsleyfy
class AccountForm(forms.ModelForm):
    def save(self, *args, **kwargs):
        # some other code...
        return forms.ModelForm.save(self, *args,**kwargs)

This should neatly avoid the issue introduced by your class decorator. 这应该整齐地避免您的类装饰器引入的问题。 Another option would be to manually call the decorator on a differently named base class, rather than using @ syntax: 另一种选择是在不同名称的基类上手动调用装饰器,而不是使用@语法:

class AccountFormBase(forms.ModelForm):
    def save(self, *args, **kwargs):
        # some other code...
        return super(AccountFormBase, self).save(*args,**kwargs)

AccountForm = parsleyfy(AccountFormBase)

However, you might also want to consider using a pre-save signal instead, depending on what you're trying to do - it's how one normally adds functionality that should happen before the rest of the model save process in Django. 但是,您可能还需要考虑使用预保存信号 ,具体取决于您尝试执行的操作 - 这是通常添加在Django中的其余模型保存过程之前应该发生的功能的方式。


As for why this is occurring, consider what happens when the code is evaluated. 至于为什么会发生这种情况,请考虑在评估代码时会发生什么。

First, a class is declared. 首先,声明一个类。 We'll refer to this original class definition as Foo to distinguish it from the later class definition that the decorator will create. 我们将这个原始的类定义称为Foo以区别于装饰器将创建的后一个类定义。 This class has a save method which makes a super(AccountForm, self).save(...) call. 这个类有一个save方法,它可以调用super(AccountForm, self).save(...)

This class is then passed to the decorator, which defines a new class which we'll call Bar , and inherits from Foo . 然后将该类传递给装饰器,装饰器定义一个我们称之为Bar的新类,并继承自Foo Thus, Bar.save is equivalent to Foo.save - it also calls super(AccountForm, self).save(...) . 因此, Bar.save相当于Foo.save - 它也称为super(AccountForm, self).save(...) This second class is then returned from the decorator. 然后从装饰器返回第二个类。

The returned class ( Bar ) is assigned to the name AccountForm . 返回的类( Bar )被分配给名称AccountForm

So when you create an AccountForm object, you're creating an object of type Bar . 因此,当您创建AccountForm对象时,您将创建一个Bar类型的对象。 When you call .save(...) on it, it goes and looks up Bar.save , which is actually Foo.save because it inherited from Foo and was never overridden. 当你在它上面调用.save(...)时,它会查找Bar.save ,它实际上是Foo.save因为它继承自Foo并且从未被覆盖过。

As we noted before, Foo.save calls super(AccountForm, self).save(...) . 正如我们之前提到的, Foo.save调用super(AccountForm, self).save(...) The problem is that because of the class decorator, AccountForm isn't Foo , it's Bar - and Bar 's parent is Foo . 问题是因为类装饰器, AccountForm不是Foo ,它是Bar - 而Bar的父级是Foo

So when Foo.save looks up AccountForm 's parent, it gets... Foo . 所以当Foo.save查找AccountForm的父级时,它会得到...... Foo This means that when it tries to call .save(...) on that parent, it actually just winds up calling itself, hence the endless recursion. 这意味着当它试图在该父节点上调用.save(...)时,它实际上只是调高自身,因此无休止的递归。

Here is what I have done to make it work, I could either change parsleyfy class to overwrite the save method like this: 以下是我为使其工作所做的工作,我可以更改parsleyfy类来覆盖save方法,如下所示:

def parsleyfy(klass):
    class ParsleyClass(klass):
        def save(self, *args, **kwargs):
            return super(klass, self).save(*args, **kwargs)
    return ParsleyClass

or change the AccountForm's save method to be like this: 或者将AccountForm的save方法更改为:

@parsleyfy
class AccountForm(forms.ModelForm):
    def save(self, *args, **kwargs):
        return super(forms.ModelForm, self).save(*args,**kwargs)

One thing I don't what the difference is, is super(Class, self) vs super(Parent, self) I have asked this question 有一件事,我没有什么区别,是super(Class, self)super(Parent, self)我问过这个问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM