未实现的 __init__()
Posted
技术标签:
【中文标题】未实现的 __init__()【英文标题】:Unimplemented __init__() 【发布时间】:2016-04-05 14:08:58 【问题描述】:我正在查看一些带有拼写错误的 __init__()
函数的代码(它总共有 3 个下划线而不是 4 个)。我意识到当一个对象找不到合适的 __init__()
时,我真的不知道 Python 中会发生什么。
是否有自动插入的默认初始化程序?是否有默认的__new__
被调用?为什么带参数的构造函数会失败?
class TestClass(object):
def do_something(self):
print("Hello From TestClass!")
# What is called here?
instance = TestClass()
instance.do_something()
# Why does this fail?
argument = 100
instance = TestClass(argument)
【问题讨论】:
你从object
继承了__init__
。
@timgeb 没有新式课程仍然是一个有效的问题。
反正OP有三个问题。我觉得两个不错,但最后一个不太好。带有参数的 ctor 将失败,因为 ctor 签名中没有参数。
为什么带参数的构造函数会失败?为什么会成功?
【参考方案1】:
是否有自动插入的默认初始化程序?
是的。除非被覆盖/继承,否则每个类都有一个默认的虚拟构造函数。
是否有默认的
__new__
被调用?
是的。同样默认情况下,除了实际创建对象和设置其类型之外,它并没有做太多事情。
为什么带参数的构造函数会失败?
因为默认构造函数不接受任何参数并且什么都不做。
【讨论】:
我认为您在重复 OP 对“初始化器”与“构造器”的混淆。当他将参数传递给它时,是他的 initializer 引发了异常。此外,类(未定义__init__
或 __new__
)并不都有自己的“虚拟”构造函数和初始化程序,但 inherit 默认来自祖先类(通常为 object
)。
@KevinJ.Chase 旧式类不继承自object
,它们确实有虚拟的__init__
和__new__
方法。我的说法仍然适用于新样式类,因为 object
显然不是新样式类。
@KevinJ.Chase 至于“初始化器”和“构造器”,我不太明白你在说什么。您似乎在谈论一些语言问题,这不是这里的重点。调用 __init__
初始化器或构造器都没关系。这些陈述仍然正确。
旧式课程与问题有什么关系?作者只使用了新式类。
@KevinJ.Chase 这是您根据发布的剪断 OP 做出的假设。 OP的问题中没有任何地方说明。我涵盖了所有可能的情况。再说一遍:我的陈述对新风格类仍然有效,因为object
是一个旧风格类。兄弟,你有什么不明白的? -.-【参考方案2】:
一切都继承自 Python 3 中的 object
,因此如果没有提供基类,则将使用默认值。
@freakish 回答了你的问题,但我会添加一个编码示例来补充他的解释。
让我们创建一个新的baseClass
,我们的新类myclass
将继承它:
class baseClass:
def __init__(self):
print("Inherited __init__ got called")
def __new__(self, *args, **kwargs):
print("Inherited __new__ got called")
return object.__new__(self, *args, **kwargs)
这个类重载了__init__
和__new__
,所以任何继承它的类都会得到这些方法。现在让我们创建一个继承自baseClass
的示例类:
class mycls(baseClass):
pass
空的,没有定义的 dunders。但是,当我们创建此类的实例时,我们可以看到 baseClass
的继承方法被调用:
m = mycls()
Inherited __new__ got called
Inherited __init__ got called
如您所见,我们将获得继承树中父项定义的所有内容。但是,因为没有显式父类的类总是继承自 object
:
class f: pass
f.__bases__ # bases gives us the inherited classes
Out[218]: (object,)
我们将始终使用定义的默认行为来处理这些方法。 对于默认的__init__
这意味着没有参数,你需要重新定义它才能自定义它!
【讨论】:
【参考方案3】:您问的是两个经常混淆的话题:初始化器__init__
和构造器__new__
。另外,我认为您将 Python 与 Java 之类的语言混淆了,其中构造函数很重要,不像其他语言,构造和初始化混合在一个进程中(因此对于 Python 新手来说是一个常见的误解)。
在 Python 中,构造器和初始化器都是方法,与任何其他方法没有区别。有一个命名约定,即(某些)两端带有双下划线的方法将由解释器调用,而无需您显式执行,但如果您愿意,您可以自己调用它们,并且您会得到相同的结果。
简而言之:如果你了解__str__
,你就了解__init__
和__new__
。
(我认为对于一些有经验的程序员来说,Python 最奇怪的地方在于它不会每时每刻都用特殊情况埋伏你。)
方法解析顺序
Python 2.3 的 new-style classes 从对象的类开始查找方法名称,然后遍历该类的祖先,直到找到要查找的名称或用完要搜索的类。
您可以通过查看其__mro__
字段来查看任何类的方法解析顺序:
>>> TestClass.__mro__
(<class '__main__.TestClass'>, <class 'object'>)
>>> float.__mro__
(<class 'float'>, <class 'object'>)
>>> object.__mro__
(<class 'object'>,)
(Python 使用C3 Method Resolution Order algorithm 构造元组,但对于像您这样的简单情况来说,它是多余的。C3 主要关注将复杂的多继承图线性化为可以按可预测顺序搜索的类元组.)
任何方法查找,即使是__init__
或__new__
,都遵循方法解析顺序。对于元组中的每个类,Python 在其类__dict__
中查找字符串(如'__init__'
);如果该字符串存在,则其关联值 --- 无论它是什么 --- 都将作为方法查找的结果返回。
在您的情况下,Python 会查找不存在的TestClass.__new__
,然后查找存在的object.__new__
。然后它执行object.__new__
(自动填写一些参数)来构造你的新TestClass
对象。使用__init__
进行初始化是相同的,只是您可以自己提供一些额外的参数。
证明(在 Python 3 中):
>>> class A: pass
...
>>> A.__init__ is object.__init__
True
>>> A.__new__ is object.__new__
True
>>> A.__repr__ is object.__repr__
True
>>> A.__str__ is object.__str__
True
__init__
的参数
您尝试将参数传递给object.__init__
失败,因为该方法不接受任何参数(self
除外,它已为您填写)。它看起来或多或少是这样的:
class Object:
def __init__(self):
return
结论
Python 不会像 (IIRC) Java 那样为您的类“创建”默认构造函数或初始化程序。但它在 object
类中各有一个,所以如果你不自己写,你会继承它们。
【讨论】:
以上是关于未实现的 __init__()的主要内容,如果未能解决你的问题,请参考以下文章
PySide.QtGui RuntimeError: '__init__' 对象基类的方法未调用...但它是