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

时间:2013-02-06 22:08:11

标签: python django forms recursion save

我正在覆盖ModelForm的保存方法,我不知道它为什么会导致递归:

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

原因:

maximum recursion depth exceeded while calling a Python object

Stacktrace显示此行反复调用自己:

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

现在,欧芹装饰师是这样的:

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

正如@DanielRoseman所说,扩展AccountForm的Parsley装饰器导致super(AccountForm,self)继续自我调用,解决方案是什么?

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

2 个答案:

答案 0 :(得分:6)

你能做的就是直接调用父方法:

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

这应该整齐地避免您的类装饰器引入的问题。另一种选择是在不同名称的基类上手动调用装饰器,而不是使用@语法:

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

AccountForm = parsleyfy(AccountFormBase)

但是,您可能还需要考虑使用pre-save signal,具体取决于您尝试执行的操作 - 通常会添加在Django中的其他模型保存过程之前应该发生的功能。


至于为什么正在发生这种情况,请考虑评估代码时会发生什么。

首先,声明一个类。我们将这个原始类定义称为Foo,以区别于装饰器将创建的后一个类定义。这个类有一个save方法,可以进行super(AccountForm, self).save(...)调用。

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

返回的班级(Bar)已分配到名称AccountForm

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

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

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

答案 1 :(得分:0)

以下是我为使其工作所做的工作,我可以更改parsleyfy类来覆盖这样的save方法:

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

或将AccountForm的save方法更改为:

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

我不知道有什么区别,super(Class, self) vs super(Parent, self)我问了这个问题question