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

Python类装饰器扩展类导致递归,python,django,forms,recursion,save,Python,Django,Forms,Recursion,Save,我正在覆盖ModelForm的save方法,不知道为什么会导致递归: @parsleyfy class AccountForm(forms.ModelForm): def save(self, *args, **kwargs): # some other code... return super(AccountForm, self).save(*args,**kwargs) 原因如下: maximum recursion depth exceeded w

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

@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
def parsleyfy(klass):
    class ParsleyClass(klass):
        def save(self, *args, **kwargs):
            return super(klass, self).save(*args, **kwargs)
    return ParsleyClass
@parsleyfy
class AccountForm(forms.ModelForm):
    def save(self, *args, **kwargs):
        return super(forms.ModelForm, self).save(*args,**kwargs)
正如@DanielRoseman所建议的那样,欧芹装饰器扩展了
AccountForm
使
super(AccountForm,self)
不断地调用自己,解决方案是什么


此外,我也无法理解这会导致递归的原因。

以下是我所做的工作,我可以更改parsleyfy类以覆盖save方法,如下所示:

def parsleyfy(klass):
    class ParsleyClass(klass):
      # some code here to add more stuff to the class
    return ParsleyClass
def parsleyfy(klass):
    class ParsleyClass(klass):
        def save(self, *args, **kwargs):
            return super(klass, self).save(*args, **kwargs)
    return ParsleyClass
@parsleyfy
class AccountForm(forms.ModelForm):
    def save(self, *args, **kwargs):
        return super(forms.ModelForm, self).save(*args,**kwargs)
或者将AccountForm的保存方法更改为:

def parsleyfy(klass):
    class ParsleyClass(klass):
      # some code here to add more stuff to the class
    return ParsleyClass
def parsleyfy(klass):
    class ParsleyClass(klass):
        def save(self, *args, **kwargs):
            return super(klass, self).save(*args, **kwargs)
    return ParsleyClass
@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)
我问过这个问题

你可以直接调用家长的方法:

@parsleyfy
class AccountForm(forms.ModelForm):
    def save(self, *args, **kwargs):
        # some other code...
        return forms.ModelForm.save(self, *args,**kwargs)
这将巧妙地避免类装饰者引入的问题。另一个选项是在不同名称的基类上手动调用decorator,而不是使用
@
语法:

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

AccountForm = parsleyfy(AccountFormBase)

但是,您可能还想考虑使用A,这取决于您想做什么——这是一个通常在Django中的模型保存过程的其余部分之前应该添加的函数。


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

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

这个类随后被传递给decorator,decorator定义了一个新类,我们称之为
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(…)
时,实际上它只会调用自身,从而导致无休止的递归。

我认为您不应该返回父对象save()方法,只需执行super(AccountForm,self)。save(*args,**kwargs)您确定这是您的实际代码吗?如果您在
super
调用中错误地引用了超类,通常会发生这种情况-例如,您实际上拥有AccountForm的子类,并且在被重写的save方法中,您仍在调用
super(AccountForm…)
。谢谢@DanielRoseman我已经更新了question@PepperoniPizza您肯定应该返回一个modelformsuper save,因为它应该返回一个instance@JamesLin
ParsleyClass
做什么?您能提供一个更完整的示例吗?@JamesLink我还添加了一个注释,这是Django添加功能的首选方法,在模型保存之前立即触发。解决此问题的另一个方法是为原始类指定一个不同的名称,然后手动调用装饰器来创建您想要使用的类(例如,
AccountForm
)。只要
super
调用使用原始类的名称(而不是修饰版本的名称),它应该按预期工作。@JamesLin在某些情况下,直接调用父方法可能会产生其他问题,特别是那些涉及调用同一类上可能已被重写的其他方法的问题。在单继承的情况下,这些问题不会弹出,但多继承可能会出现“这不是很棘手。@杰梅斯林我用一个布尔克恩赫特建议的例子扩展了我的答案。@JamesLin
super()的复杂性
太复杂了,无法真正放在一个注释线程中;我建议你通读一遍,如果你真的想知道更多的话。简而言之,当你使用多重继承(多个超类,例如
类C(a,B):
)时,“父类”的概念变得更加复杂。