除非明确要求,否则 Python 从不进行复制(例如,对列表进行切片确实要求 Python 浅层复制列表的该部分)。
“add_two 是否引用 n 所指向的同一个 int 对象?” -- 是的,仅参考该内容int
被传递并保存在框架中。在这种情况下,无论值是多少,这都适用n
.
任何 Python 实现都可以保留不可变对象行的单个副本或多个副本int
s——只要语义不受影响,就可以采用最方便的实现方式。
因此,在给定的实现中,每次提到文字都可能发生3
指的是同一个int
对象但提及文字333
不需要。例如:
2>>> a=333; b=333; print(id(a), id(b))
(4298804944, 4298804944)
2>>> a=333
2>>> b=333
2>>> print(id(a), id(b))
(4298753600, 4298753336)
两种情况的语义完全相同;在第一种情况下,编译器(本质上一次在整行上调用)发现实例化和使用单个int
worth 333
,在第二种情况下,它更喜欢创建并使用两个这样的实例——任何一个都完全可以,因为int
的不变性(这同样适用于其他数字类型、字符串、元组、冻结集——但不适用于可变类型)。
请注意,当 Python 规范提到“相同语义”时,它明确包含内省,这可能能够查明语义等效状态之间的实现差异。
id
(在当前流行的 Python 实现中,通常返回对象的内存地址,但在任何情况下,只要该对象存在,根据语言规范,每个对象的 id 都是唯一的)是内省,因此is
操作员。因此,如果您希望使用它来了解给定实现可能执行或不执行的某些优化,则可以。
那么,关于你的其他问题:“我的理解正确吗?” - 不。
“为什么会出现这种差异”——def
构建一个函数对象,它是可变的,所以任何def
即使具有相同的函数定义也必须返回一个新对象,就像例如[]
构建一个列表对象,可变的,所以任何[]
必须返回一个新对象。3
建立一个int
对象,它是不可变的,所以任何3
允许(根据语言规则)返回相同或新的对象。
在编辑中添加了另一个问题:“返回函数类型对象时堆栈帧处于活动状态的想法是什么?”
答案:只要可到达,每个对象都会保持活动状态。特别是,只要返回内部(嵌套)函数(如果它们引用外部框架中的名称),外部函数的框架就会保持活动状态。
(任何 Python 实现都不会have垃圾收集不再需要存活的对象——它可以根据需要延迟垃圾收集,或者可以立即执行它——实现细节!-)。