Python语句x=x+;1实施?

Python语句x=x+;1实施?,python,garbage-collection,Python,Garbage Collection,在C语言中,语句x=x+1将更改为x分配的同一内存中的内容。但是在Python中,由于变量可以有不同的类型,=左侧和右侧的x可能有不同的类型,这意味着它们可能引用不同的内存块。如果是这样,在x将其引用从旧内存更改为新内存后,旧内存可以通过垃圾收集机制回收。如果是这种情况,以下代码可能会多次触发垃圾收集过程,因此效率非常低: for i in range(1000000000) i=i+1 我的猜测正确吗 更新: 我需要更正代码中的拼写错误,以使问题更清楚: x=0 for i in r

在C语言中,语句x=x+1将更改为x分配的同一内存中的内容。但是在Python中,由于变量可以有不同的类型,=左侧和右侧的x可能有不同的类型,这意味着它们可能引用不同的内存块。如果是这样,在x将其引用从旧内存更改为新内存后,旧内存可以通过垃圾收集机制回收。如果是这种情况,以下代码可能会多次触发垃圾收集过程,因此效率非常低:

for i in range(1000000000)
    i=i+1
我的猜测正确吗

更新:

我需要更正代码中的拼写错误,以使问题更清楚:

x=0
for i in range(1000000000)
    x=x+1

@SvenMarnach,你是说如果没有激活垃圾收集,整数0,1,2,…,999999999(标签x曾经提到)都存在于内存中吗?

id
可以用来跟踪内存对对象的“分配”。应该谨慎使用,但在这里我认为它很有启发性
id
有点像
c
指针——也就是说,与对象在内存中的“位置”有多大关系

In [18]: for i in range(0,1000,100): 
    ...:     print(i,id(i)) 
    ...:     i = i+1 
    ...:     print(i,id(i)) 
    ...:                                                                        
0 10914464
1 10914496
100 10917664
101 10917696
200 10920864
201 10920896
300 140186080959760
301 140185597404720
400 140186080959760
401 140185597404720
...
900 140186080959760
901 140185597404720
In [19]: id(1)                                                                  
Out[19]: 10914496

小整数(你基本上是正确的,尽管我认为一些澄清可能会有所帮助

首先,Python中C中变量的概念有很大不同。在C中,变量通常引用内存中的固定位置,正如您自己所说。在Python中,变量只是一个可以附加到任何对象的标签。一个对象可以有多个这样的标签,或者根本没有,标签可以在对象之间自由移动。assiC中的gnment将新值复制到内存位置,而Python中的赋值将新标签附加到对象

在两种语言中,整数也有很大的不同。在C语言中,整数的大小是固定的,并且以硬件固有的格式存储整数值。在Python中,整数具有任意精度。它们存储为“数字”数组(在CPython中通常为30位整数)与存储类型信息的Python类型头一起使用。大整数将比小整数占用更多内存

此外,Python中的整数对象是不可变的,一旦创建它们就不能更改。这意味着每个算术运算都会创建一个新的整数对象。因此,代码中的循环确实会在每次迭代中创建一个新的整数对象

然而,这并不是唯一的开销。它还在每次迭代中为
i
创建一个新的整数对象,该对象被丢弃在循环体的末尾。并且算术运算是动态的–Python需要查找
x
的类型及其
\uuuuuu添加()
方法,以确定如何添加这种类型的对象。Python中的函数调用开销相当高

另一方面,在CPython中,垃圾收集和内存分配相当快。整数的垃圾收集完全依赖于引用计数(这里不可能有引用周期)对于分配,CPython使用arena分配器处理小型对象,这些对象可以快速重用内存插槽,而无需调用系统分配器


总之,是的,与C中的相同代码相比,这段代码在Python中的运行速度会非常慢。现代C编译器只需在编译时计算此循环的结果并将结果加载到寄存器,因此它基本上会立即完成。如果整数算术的原始速度是您想要的,请不要用Python编写该代码。

hink no……你的假设基本上是正确的。每次迭代都会创建一个必须进行垃圾收集的新对象。但垃圾收集通常不会一直发生。Python使用引用计数。因此“垃圾收集过程”可能没有那么昂贵()。此外,关于变量如何工作的推理,还有一个有趣的思维过程。也许你也可以读一下这个,尽管Python与其他语言有点不同:在这个循环中,每个循环都会“创建”一个新的整数,当你进行
i+1
calc时,同样如此。通常,在lo中重新分配迭代变量是不好的做法op.但通常我们不会对
gc
细节大惊小怪。在
cpython
中,小整数是“唯一的”(缓存可能适用),大于255的每次使用都是“创建”新的。检查循环中
i
id
,似乎解释器正在“重用”循环中的内存。这也是一个实现细节。超级挑剔:“有趣的是,id(1)”中的常量1具有相同的id(内存)当i=1时作为变量i的id。我想知道“i=i+1”中的常量1是否是这样的“+1”,但“不要关注细节”值得做一点限定。GC和内存分配通常意味着性能良好的程序和速度较慢的程序之间的区别。我记得一次Java性能调优演示,演讲者声称对特定大型代码库的大多数简单修复都涉及最小化对象分配,通常是在循环中。类似地,Python的一个简单调整是请参阅通过重复字符串连接来连接字符串列表。我喜欢您发现的on/off id用法,但在OP的非真实示例中更容易理解。CPython的大整数实现主要使用30位“数字”"而不是现在的15位数字。@MarkDickinson我有一段时间没有看Python代码库了。我认为在古代的某个时候它是15位的,但我应该仔细检查一下。是的,它变了。不幸的是,它仍然处于一种状态,有时是15位数字,有时是30位;这可能会更好呃,如果改变是无条件的。事后诸葛亮等等。
In [20]: id(202)                                                                
Out[20]: 10920928     # same id as in the loop
In [21]: id(302)                                                                
Out[21]: 140185451618128   # different id
In [22]: id(901)                                                                
Out[22]: 140185597404208
In [23]: id(i)                                                                  
Out[23]: 140185597404720   #  = 901, but different id