Python类装饰器扩展类导致递归 [英] Python class decorator extending class causes recursion

查看:99
本文介绍了Python类装饰器扩展类导致递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



pre $ @parsleyfy
class AccountForm(forms.ModelForm):
def save(self,* args,** kwargs):
#some other代码...
返回超级(AccountForm,self).save(* args,** kwargs)

导致这种情况:

 调用Python对象时,超过最大递归深度

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

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

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

  def parsleyfy(klass):
class ParsleyClass(klass):
#一些代码在这里添加更多的东西到类
返回ParsleyClass

As @DanielRoseman建议欧芹装饰师延伸 AccountForm 导致 super(AccountForm,self)继续调用自己,有什么解决方案?


解决方案

你可以做的只是打电话给父母的方法直接:

  @parsleyfy 
class AccountForm(forms.ModelForm):
def save(self ,* args,** kwargs):
#一些其他代码...
返回forms.ModelForm.save(self,* args,** kwargs)

这应该整洁地避免你的类装饰器引入的问题。另一个选择是手动调用不同命名的基类的装饰器,而不是使用 @ 语法:

  class AccountFormBase(forms.ModelForm):
def save(self,* args,** kwargs):
#一些其他代码...
return super(AccountFormBase,self).save(* args,** kwargs)

AccountForm = parsleyfy(AccountFormBase)

但是,您也可以考虑使用预先保存信号,这取决于您正在尝试的内容 - 这是通常在Django中剩余的模型保存过程之前通常添加的功能。






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



首先,声明一个类。我们将把这个原始类的定义称为 Foo ,以区别装饰器创建的后期类定义。这个类有一个保存方法,它使一个超级(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 / p>

所以当 Foo.save 查找 AccountForm 父母,它得到... Foo 。这意味着当它尝试调用该父代的 .save(...)时,它实际上只是调用自己的调用,因此无休止的递归。


I'm overwriting the save method of a ModelForm and I don't know why it would cause recursion:

@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:

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?

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.


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.

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.

The returned class (Bar) is assigned to the name 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.

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.

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.

这篇关于Python类装饰器扩展类导致递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆