我最近一直在学习更多关于Python的知识,并且正如我通过优秀的Dive into Python作者所指出的那样here __init__
方法在技术上并不是构造函数,即使它通常的功能就像一个。
我有两个问题:
如何区别? C ++构造一个对象以及如何构造 Python“构造”一个对象?
什么
使构造函数成为构造函数
以及__init__
方法如何
不符合这个标准?
答案 0 :(得分:15)
作者的区别在于,就Python语言而言,在之前,您有一个指定类型的有效对象,甚至可以输入__init__
。因此,它不是“构造函数”,因为在C ++中,理论上,构造函数将无效的预构造对象转换为该类型的“正确”完成对象。
基本上,Python中的__new__
被定义为返回“新对象实例”,而C ++新运算符只返回一些内存,这还不是任何类的实例。
然而,Python中的__init__
可能是您首先建立一些重要的类不变量(它具有哪些属性,仅适用于初学者)。因此,就您的类的用户而言,它可能也是一个构造函数。只是Python运行时不关心任何这些不变量。如果您愿意,它对构造对象的构成标准非常低。
我认为作者提出了一个公平的观点,这对于Python创建对象的方式肯定是一个有趣的评论。这是一个非常好的区别,但我怀疑调用__init__
构造函数会导致代码损坏。
另外,我注意到Python文档将__init__
称为构造函数(http://docs.python.org/release/2.5.2/ref/customization.html)
作为特殊约束 构造函数,没有值可以返回
...所以如果将__init__
视为构造函数存在任何实际问题,那么Python就遇到了麻烦!
Python和C ++构造对象的方式有一些相似之处。两者都调用一个功能相对简单的函数(对象实例__new__
对原始内存的某些版本operator new
),然后调用一个有机会做更多工作来初始化对象的函数进入一个有用的状态(__init__
vs构造函数)。
实际差异包括:
在C ++中,必要时会以适当的顺序自动调用基类的no-arg构造函数,而对于Python中的__init__
,您必须在自己的{{1}中显式地初始化基数}。即使在C ++中,如果它有参数,也必须指定基类构造函数。
在C ++中,对于构造函数抛出异常,在为已经构造的子对象调用析构函数方面,你有一个完整的机制。在Python中,我认为运行时(最多)调用__init__
。
然后还有一个区别是__del__
不只分配内存,它必须返回一个实际的对象实例。然后,原始内存并不是一个适用于Python代码的概念。
答案 1 :(得分:2)
许多其他语言的构造函数为正在构造的对象分配空间;在Python中,这是分配器方法__new__()
的工作。 __init__()
只是一种初始化方法。
答案 2 :(得分:2)
在Python中,__new__
创建了一个对象,__init__
修改了这种通用默认对象。而__init__
只是一种普通的方法。特别是它可以虚拟调用,而__init__
的调用方法实际上会调用它们。
在C ++中,对象的原始内存以某种方式,静态或调用堆栈分配,或通过operator new
动态分配,或作为另一个对象的一部分分配。然后,您要实例化的类型的构造函数将原始内存初始化为合适的值。给定类的构造函数自动调用基类和成员的构造函数,因此构造保证是“自下而上”构造,使部件成为第一。
C ++为部分构建理念的两个特别重要的方面添加语言支持:
T
的构造函数体时,对象的类型为T
,因此对虚方法的调用将解析为对象的类型为T
(它是,在这一点上),其中T
可以是您实例化的类的基类。第一点意味着使用设计合理的C ++类,当手头有一个对象时,它保证可以原样使用。如果构造失败,那么你根本就不会得到一个手头的物体。
此外,C ++的规则旨在确保对于大多数派生类T
的每个对象,只有一个T
构造函数调用。我以前称之为单个构造函数调用保证。它没有在标准中指定为任何地方,你可以通过使用语言的非常低级别的设施来阻止它,但它就在那里,这是标准的详细规则旨在实现的(它与你赢得的相同)找不到任何关于语句分号终止的规则,但各种语句的所有无数语法规则都会产生一个简单的高级规则。)
单个构造函数调用保证,自动清理保证以及作为基类构造函数的对象的更改类型是可能的,这可能是与Python对象构造的三个最重要的区别。
还有更多要说的内容,但我认为这些是最重要的想法。
干杯&第h。,
答案 3 :(得分:-1)
差异是由于Python的动态类型。与C ++中使用类型声明变量并在内存中分配变量不同,Python的变量是在运行时分配时创建的。自动调用C ++ no-arg构造函数,以便它们可以初始化数据成员。在Python中,它是按需完成的,并且__init__
由继承树查找,只有最低的树被调用一次。如果需要超类数据属性,则像C++ initialization list这样显式地调用super().__init__()
。下面是初始化Base.s的示例:
class Base:
def __init__(self, base):
print("Base class init called")
self.base = base
class Super(Base):
def __init__(self, base):
print("Super class init called")
#super(Super, self).__init__(base)
super().__init__(base)
class Sub(Super):
def __init__(self, base):
print("Default __init__ is called")
super().__init__(base)
sub = Sub({"base3": 3, "base4": 4})
print(sub.__dict__, sub.base)
输出:
Default __init__ is called
Super class init called
Base class init called
{'base': {'base3': 3, 'base4': 4}} {'base3': 3, 'base4': 4}
此外, init 就像普通函数一样,之后可以这样调用:
Sub.__init__(sub, 'abc')
print(sub.__dict__, sub.base)
输出:
Default __init__ is called
Super class init called
Base class init called
{'base': 'abc'} abc