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

时间:2020-12-01 18:14:50

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.

此外,我无法理解为什么这会导致递归。

2 个解决方案

#1


6  

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. This class has a save method which makes a super(AccountForm, self).save(...) call.

首先,声明一个类。我们将这个原始的类定义称为Foo,以区别于装饰器将创建的后一个类定义。这个类有一个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. Thus, Bar.save is equivalent to Foo.save - it also calls super(AccountForm, self).save(...). This second class is then returned from the decorator.

然后将该类传递给装饰器,装饰器定义一个我们称之为Bar的新类,并继承自Foo。因此,Bar.save相当于Foo.save - 它也称为super(AccountForm,self).save(...)。然后从装饰器返回第二个类。

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. 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.

因此,当您创建AccountForm对象时,您将创建一个Bar类型的对象。当你在它上面调用.save(...)时,它会查找Bar.save,它实际上是Foo.save,因为它继承自Foo并且从未被覆盖过。

As we noted before, Foo.save calls 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.

正如我们之前提到的,Foo.save调用super(AccountForm,self).save(...)。问题是因为类装饰器,AccountForm不是Foo,它是Bar - 而Bar的父级是Foo。

So when Foo.save looks up AccountForm's parent, it gets... Foo. This means that when it tries to call .save(...) on that parent, it actually just winds up calling itself, hence the endless recursion.

因此,当Foo.save查找AccountForm的父级时,它会得到...... Foo。这意味着当它试图在该父节点上调用.save(...)时,它实际上只是自动调用,因此无限递归。

#2


0  

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

有一点我没有什么区别,是超级(Class,self)vs super(Parent,self)我问过这个问题

#1


6  

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. This class has a save method which makes a super(AccountForm, self).save(...) call.

首先,声明一个类。我们将这个原始的类定义称为Foo,以区别于装饰器将创建的后一个类定义。这个类有一个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. Thus, Bar.save is equivalent to Foo.save - it also calls super(AccountForm, self).save(...). This second class is then returned from the decorator.

然后将该类传递给装饰器,装饰器定义一个我们称之为Bar的新类,并继承自Foo。因此,Bar.save相当于Foo.save - 它也称为super(AccountForm,self).save(...)。然后从装饰器返回第二个类。

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. 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.

因此,当您创建AccountForm对象时,您将创建一个Bar类型的对象。当你在它上面调用.save(...)时,它会查找Bar.save,它实际上是Foo.save,因为它继承自Foo并且从未被覆盖过。

As we noted before, Foo.save calls 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.

正如我们之前提到的,Foo.save调用super(AccountForm,self).save(...)。问题是因为类装饰器,AccountForm不是Foo,它是Bar - 而Bar的父级是Foo。

So when Foo.save looks up AccountForm's parent, it gets... Foo. This means that when it tries to call .save(...) on that parent, it actually just winds up calling itself, hence the endless recursion.

因此,当Foo.save查找AccountForm的父级时,它会得到...... Foo。这意味着当它试图在该父节点上调用.save(...)时,它实际上只是自动调用,因此无限递归。

#2


0  

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

有一点我没有什么区别,是超级(Class,self)vs super(Parent,self)我问过这个问题