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

标签 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) 继续调用自身,解决方案是什么?

我也无法理解为什么这会导致递归。

最佳答案

你可以做的就是直接调用父类的方法:

@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(...) 时,它实际上只是调用自身,因此无休止的递归。

关于Python类装饰器扩展类导致递归,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14739809/

相关文章:

javascript - 输入值不显示在字段中

javascript - 使用 javascript 和按钮 onclick 生成新表单

php - 为什么隐藏元素中有这个奇怪的符号

python - 如何转换也包含整数 NaN 的年份行?

python - 打开 CV 或用于后处理识别的替代方法

android - 在机器人中将 python 函数作为关键字运行会导致无限循环

python - 如何使用 django 删除图像?

Django 聚合 - 表达式包含混合类型。您必须设置 output_field

python - 如何更改 Django 模型中的选项?

python 使用curl上传文件