__init__作为构造函数?

63 投票
6 回答
44606 浏览
提问于 2025-04-16 20:50

Dive into Python -

我们可能会觉得可以把这个叫做类的构造函数,但其实这样理解是不对的。之所以会有这种感觉,是因为它看起来像构造函数(按照惯例,__init__ 是类中定义的第一个方法),而且它的作用也像构造函数(在新创建的类实例中,它是第一个被执行的代码),甚至名字听起来也像构造函数(“init”确实让人联想到构造函数的意思)。但其实不对,因为在调用 __init__ 的时候,对象已经被构造好了,你已经有了对这个新实例的有效引用。

这段话的意思是说,称 __init__ 为构造函数是不准确的,因为在调用 __init__ 时,对象已经被创建好了。不过!我一直以为构造函数是在对象构造完成后才被调用的,因为它主要是用来初始化实例的数据成员,如果对象在构造函数被调用时还不存在,那就没道理了。(我之前是学 C++/Java 的)

6 个回答

6

构造函数会返回一个实例,并且可能会失败。但是,__init__ 并不会返回实例。即使 __init__ 报错了,__del__ 还是会被调用来删除这个实例。

你可以在这里看到:

class A(object):
    def __init__(self):
        raise ValueError

    def __del__(self):
        print "Called"

def main():
    try:
        a = A()
    except ValueError, e:
        print "ValueError"

if __name__ == '__main__':
    main()

另一方面,__new__ 是会返回一个实例的。

54

我个人觉得“__init__ 不是构造函数”这个说法有点过于较真了。

当你请求创建一个新对象时,__init__ 会被调用。它的作用是用传入的参数来给新对象设置属性,确保这个对象能正常工作。在__init__ 代码开始执行时,这个新对象已经是一个有效的地方来存储属性了。不过在__init__ 开始运行时,这个新对象通常还没有定义任何属性(除了所有对象都有的那些属性)。

在C++中,当你请求创建一个新对象时,会调用构造函数。它的作用也是用传入的参数来给新对象的字段赋值,以确保对象能正常工作。在构造函数代码开始运行时,这个新对象已经是一个有效的地方来存储字段了。不过在构造函数开始运行时,这个新对象已经有了所有声明的字段,但它们的值是垃圾数据。

在Java中,当你请求创建一个新对象时,同样会调用构造函数。它的作用也是用传入的参数来给新对象的字段赋值,以确保对象能正常工作。在构造函数代码开始运行时,这个新对象已经是一个有效的地方来存储字段了。不过在构造函数开始运行时,这个新对象已经有了所有声明的字段,并且这些字段有默认值。

__init__ 方法和C++/Java构造函数之间的主要区别就在于我强调的那句话,这其实是Python的动态特性和Java/C++的静态特性之间的差别。我觉得这并不需要把它们称为完全不同的概念,不能用同一个词来描述。

我认为Python开发者不喜欢把__init__称为构造函数的主要原因是,人们通常把C++/Java的构造函数理解为“创建一个新对象”,因为调用它们时确实是这样。但实际上,调用构造函数时有两个步骤:首先创建一个新对象,然后调用构造函数来初始化它。在C++/Java中,“创建新对象”这一步是看不见的,而在Python中,这个过程是可以被展示和自定义的(通过__new__方法)。

所以虽然__init__ 方法的作用和C++/Java构造函数非常相似,但有些人更喜欢强调这不是整个过程,因此说“__init__ 不是构造函数”。

57

如果你有一个类叫做 Foo,那么:

  • Foo() 是这个类的构造函数
  • Foo.__init__() 是这个类的初始化函数
  • Foo.__new__() 是这个类的分配器

在Python中,创建一个对象其实就是先分配一个新的实例,然后再对这个实例进行初始化。

撰写回答