Python中super()的行为不可靠

41

由于某些原因,super()方法并不总是按预期运行,选择返回:

TypeError('super(type, obj): obj must be an instance or subtype of type)'

我理解这个错误的含义,但不理解为什么会出现这个错误。下面是导致问题的代码片段。系统中所有对象都是新风格对象。

有趣的是,这个错误并不总是显示。我不知道是什么原因导致它。在Retrieval中的super()方法正在传递Retrieval类,然后将其本身作为一个对象传递,据我所知,这正是super()应该调用的方式。

你有任何想法吗?

在文件DBConnection.py中:

class DBAdminConnection(object):
    def __init__(self):
        self.user = DBUserConnection().user 
        self.submissions = DBSubmissionConnection()

在文件Retrieval.py

class Retrieval(DBConnection.DBAdminConnection): 
    def __init__(self, username=None, password=None, unique_key=None):
        super(Retrieval,self).__init__()
        if username and password:
            self.username = username
            self.user.login(username,password, config.DATABASE)
            if self.user.error:
                raise UserLoginError(username)
        self.unique_key = unique_key
5个回答

57

你是否在进行操作中重新加载模块?如果是这样,那可能会解释这个错误。

isinstance(self, DBAdminConnection)由于内存引用的更改,很可能在重新加载模块后变为false。

编辑:如果您正在mod_wsgi下运行您的web.py应用程序,请确保禁用autoreload:

app = web.application(urls, globals(), autoreload=False)

这是通过webpy运行的。所以这是一个明显的可能性。特别是因为如果我杀死应用程序实例并重新启动它,问题就会消失。哇。现在的问题变成了如何修复它。 - Thomas Thorogood
@TomThorogood:你是如何运行你的应用程序的?你使用mod_wsgi吗? - Eduardo Ivanec
我不认为我正在这样做。我现在会添加它!我没有使用mod_wsgi。我正在使用带有spawn-fcgi的nginx。我将尝试禁用自动重新加载。非常感谢! - Thomas Thorogood
我将标记此问题为已解决;您提出的所有建议都非常合理,我相信这个解决方案是唯一的(除非webpy存在漏洞)。 - Thomas Thorogood
14
发生了与你所说的完全相同的事情,因为我在IPython中使用了%load_ext autoreload%autoreload 2类。 - P.R.

12
如果您在工作流程中使用了reload(),那么您显然还需要使用super(self.__class__, self).__init__进行继承初始化。
我怀疑你会发现这个bug与id(self.__class__) == id(Retrieval)的失败有关。

8
super(self.__class__, self) 总是错误的。如果您进一步创建子类,它将导致无限循环。 - Blckknght
1
如果您不再进行任何子类化,那么这样做有时可以吗? - cce

1

我不确定为什么会出现错误,但作为调试的辅助,您可以将对super的调用包装在一个try/except块中,并在引发异常时进行数据转储。就像这样:

class Retrieval(DBConnection.DBAdminConnection): 
    def __init__(self, username=None, password=None, unique_key=None):
        try:
            super(Retrieval,self).__init__()
        except TypeError, e:
            print "Failure initialising Retrieval --> self: %r"
            raise
        if username and password:
            self.username = username
            self.user.login(username,password, config.DATABASE)
            if self.user.error:
                raise UserLoginError(username)
        self.unique_key = unique_key

你的意思是你不确定吗?因为你让我对此抱有希望。/微笑 - Thomas Thorogood
哈哈,没问题!谢谢你的东西。我已经放置了一些调试代码;我只是在为 Stack Overflow 整理代码。 - Thomas Thorogood

1
我刚遇到了同样的问题,在jupyter笔记本中运行我的代码。我正在使用reload,所以我重新启动了内核,以便跟进Eduardo Ivanec的回复,试图看看这是否是问题所在。然后我的代码就出错了。我发现我的问题与多层继承有关,其中底层在模块中定义,而第二个底层在其上方定义。
class MyClass1(object):
'''example class 1'''

class MyClass2(MyClass1):
'''example class 2'''
    def __init__(self):
    super(MyClass2, self).__init__()

class MyClass4(MyClass3):
'''example class 4 - no __init__ definition'''

class MyClass3(MyClass2):
'''example class 3 - no __init__ definition'''

当我将MyClass4移动到MyClass3下面时,问题得到了解决。
这可能是初学者的错误,因此它可能无法解决上述原始问题的原因,但是我想发布帖子,以防有其他像我一样的新手犯同样的错误。
(如果我的风格有误,对不起,这是我在Stack Overflow上的第一篇帖子。:))

1
我曾经遇到同样的错误,后来发现在同一个file.py文件中有重复的类(我的错误)。当我将第二个类A重命名为类B时,错误消失了。
#Just some example below, not real code
class A (object):

    def fun(self):
        super(A, self).fun()

class A (object): ##This second class with same name (A) caused the error

   def some_fun(self,x,y):

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接