python如何在'for'循环中处理对象实例化

Gau*_*lio 7 python for-loop memory-management reference-counting instantiation

我有一个非常复杂的课程:

class C:
    pass
Run Code Online (Sandbox Code Playgroud)

我有这个测试代码:

for j in range(10):
    c = C()
    print c
Run Code Online (Sandbox Code Playgroud)

这使 :

<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
<__main__.C instance at 0x7f7336a6cb00>
<__main__.C instance at 0x7f7336a6cab8>
Run Code Online (Sandbox Code Playgroud)

人们可以很容易地看到Python切换到两个不同的值.在某些情况下,这可能是灾难性的(例如,如果我们将对象存储在其他复杂对象中).

现在,如果我将对象存储在List中:

lst = []
for j in range(10):
    c = C()
    lst.append(c)
    print c
Run Code Online (Sandbox Code Playgroud)

我明白了:

<__main__.C instance at 0x7fd8f8f7eb00>
<__main__.C instance at 0x7fd8f8f7eab8>
<__main__.C instance at 0x7fd8f8f7eb48>
<__main__.C instance at 0x7fd8f8f7eb90>
<__main__.C instance at 0x7fd8f8f7ebd8>
<__main__.C instance at 0x7fd8f8f7ec20>
<__main__.C instance at 0x7fd8f8f7ec68>
<__main__.C instance at 0x7fd8f8f7ecb0>
<__main__.C instance at 0x7fd8f8f7ecf8>
<__main__.C instance at 0x7fd8f8f7ed40>
Run Code Online (Sandbox Code Playgroud)

这解决了这个问题.

所以现在,我不得不问一个问题......有没有人可以解释复杂的单词(我的意思是,深入)Python如何使用对象引用?我想,这是一个优化问题(为了节省内存,或防止泄漏......)

非常感谢.

编辑: 好的,让我们更具体一点.我很清楚python有时候会收集垃圾......但是,在我的情况下:

我有一个由Cython定义的类返回的列表:类'Network'管理'Node'列表(Network和Node类都在a中定义Cython extension).每个节点都有一个对象[然后被转换为(void*)]'userdata'对象.节点列表从cython内部填充,而UserData填充在Python脚本中.所以在python中,我有以下内容:

...
def some_python_class_method(self):
    nodes = self.netBinding.GetNetwork().get_nodes()
    ...
    for item in it:
        a_site = PyLabSiteEvent()
        #l_site.append(a_site)        # WARN : Required to get an instance on 'a_site' 
                                      #        that persits - workaround...
    item.SetUserData(a_site)
Run Code Online (Sandbox Code Playgroud)

稍后使用相同的cython getter在同一个python类中重用此节点列表:

def some_other_python_class_method(self, node):
    s_data = node.GetUserData()
    ...
Run Code Online (Sandbox Code Playgroud)

因此,似乎使用节点列表的UserDatas中的存储,我的python脚本完全失明并释放/重用内存.它通过第二次引用(但显然是python方面的第一次),使用额外的列表(这里:'l_site').这就是为什么我必须更多地了解Python本身,但似乎我实现Python之间的通信的方式,Cython并负责必须面对的问题.

jsb*_*eno 13

这里没有必要"复杂":在第一个例子中,你不保留名称"c"引用的对象的其他引用 - 在后续迭代中运行"c = C()"行中的代码时循环,之前保存在"c"中的一个参考丢失.

由于标准Python使用引用计数来跟踪它应该从内存中删除对象的时间,因为此时前一个循环交互的对象的引用计数达到0,它被销毁,并且其内存可用于其他对象.

为什么你有两个不断变化的值?因为此时新迭代中的对象被创建 - 即当Python将表达式执行到=in 的右侧时c = C(),预先存在的迭代的对象仍然存在,由名称引用c- 因此新对象在另一个内存中构造locaton.Python然后继续分配新对象c,此时如上所述销毁前一个对象 - 这意味着在下一个(第三个)迭代中,该内存将可用于新的实例C.

在第二个示例中,新创建的对象永远不会松散引用,因此根本不释放它们的内存 - 新对象总是占用新的内存位置.

最重要的是: 使用高级语言如Python或其他的目的,是不是不必担心内存分配.语言会照顾你.在这种情况下,正如您所注意到的那样,CPython(标准)实现会做正确的事情.其他实现(如Pypy或Jython)在上述示例中的每个实例的"内存位置"方面可能有完全不同的行为,但所有符合实现的(包括这3个)将从"视点"的"视角"完全相同. Python程序:(1)它可以访问它保持引用的实例,(2)这些实例的数据无论如何都没有被破坏或损坏.


Lar*_*tig 5

它似乎并不复杂.

在第一个示例中,第二次通过循环,0x7f7336a6cb00处的内存被第一次迭代期间创建的C实例占用.因此,Python为新C对象分配下一个内存块0x7f7336a6cab8.

但是,只要创建第二个C对象并将其分配给c0x7f7336a6cb00,就不会有对现在孤立对象的引用.因此,第三次通过循环Python可以重新使用此位置的内存作为新对象.当然,当它这样做时,0x7f7336a6cab8处的对象不再具有对它的引用,并且该存储器位置可以通过循环第四次回收.

但是,在第二个示例中,通过将对象附加到列表,您将保留对您创建的每个对象的引用.由于这些对象始终至少有一个对它们的引用,因此它们"存在"的内存永远不可用于释放和回收.因此Python每次都会分配新的内存.

在第一个例子中产生的危险幻觉只是 - 一种幻觉.只要对您创建的对象存在引用,就会维护该对象.当不存在任何引用时,Python释放对象使用的内存是安全的,因为程序不可能再使用该对象.