首页 > 编程语音, 贝壳 > 关于Python变量和List的append操作

关于Python变量和List的append操作

2012年2月28日 发表评论 阅读评论

今天,写Python代码,碰到一个很诡异的问题,花了好长时间才明白(也怪我基础不好,没系统学习python,都是用的时候直接抓脑皮用),通过这个问题,我基本搞清楚了Python中的变量以及List的一些操作。在这里给自己Mark下,也希望对读这篇文章的人有点用。
首先,看下面这段代码:

#!/usr/bin/python
# -*- coding: utf-8 -*- 

import os,sys

trace_file = open("./all_memtrace_file_test","r")

all_addr=[]
all_addr2=[]
addr_info2={}
addr_info={}

for line in trace_file.readlines():
    addr_info = {'addr':int(line.split(",")[2].strip(" "),16),'access':line.split(",")[3].strip("n")}
#    addr_info2={}
    addr_info2['addr']=int(line.split(",")[2].strip(" "),16)
    addr_info2['access']=line.split(",")[3].strip("n")
    if addr_info == addr_info2:
        print "addr_info == addr_info2"
    print "addr_info  = ",
    print addr_info
    print "addr_info2 = ",
    print addr_info2
    all_addr.append(addr_info)
    all_addr2.append(addr_info2)
    print "all_addr   = ",
    print all_addr
    print "all_addr2  = ",
    print all_addr2

其中./all_memtrace_file_test的内容如下:

 0,              49, 0x7fdc000,l
 0,              52, 0x20049c4,l

咋一看,all_addr和all_addr2应该是一样的,结果应该是:

addr_info == addr_info2
addr_info  =  {'access': 'l', 'addr': 134070272}
addr_info2 =  {'access': 'l', 'addr': 134070272}
all_addr   =  [{'access': 'l', 'addr': 134070272}]
all_addr2  =  [{'access': 'l', 'addr': 134070272}]
addr_info == addr_info2
addr_info  =  {'access': 'l', 'addr': 33573316}
addr_info2 =  {'access': 'l', 'addr': 33573316}
all_addr   =  [{'access': 'l', 'addr': 134070272}, {'access': 'l', 'addr': 33573316}]
all_addr2  =  [{'access': 'l', 'addr': 134070272}, {'access': 'l', 'addr': 33573316}]

而实际上,结果确实第二次循环时,all_addr和all_addr2不一样,结果是:

addr_info == addr_info2
addr_info  =  {'access': 'l', 'addr': 134070272}
addr_info2 =  {'access': 'l', 'addr': 134070272}
all_addr   =  [{'access': 'l', 'addr': 134070272}]
all_addr2  =  [{'access': 'l', 'addr': 134070272}]
addr_info == addr_info2
addr_info  =  {'access': 'l', 'addr': 33573316}
addr_info2 =  {'access': 'l', 'addr': 33573316}
all_addr   =  [{'access': 'l', 'addr': 134070272}, {'access': 'l', 'addr': 33573316}]
all_addr2  =  [{'access': 'l', 'addr': 33573316}, {'access': 'l', 'addr': 33573316}]

我想了好久,才明白这是为什么。要明白具体的原因,需要了解Python变量以及append函数的知识,具体为:

  1. python变量其实都是“引用”(reference),并不会实际给其分配内存空间。
  2. python变量作用域遵循LGB原则,即先局部(local scope),后全局(global scope)。
  3. python中,只要是对字典全局赋值(右值不为字典变量之前描述有误,在rex提醒下,应为右值不为字典变量的某一项),就是创建了一个字典,会申请一块内存,并且将该字典变量指向该内存。如前述代码14行,就定义了一个新的局部字典变量addr_info。根据LGB原则,此后在该代码区块中使用的addr_info就是这个局部变量。
  4. 假如只对字典某一项赋值,是更新字典,如代码中16,17行。因此addr_info2还是全局变量,没有定义新的变量。
  5. list的append函数,就是把变量(也就是引用,相当于指针)加在列表后面。因此,对于all_addr,由于两次执行14行语句,都创建了变量,申请的新的内存,因此append上的两个变量所指向的内存是不同的。而对all_addr2,由于都是append全局的addr_info2,导致实际上列表上两项指向的是相同的内存区块,其值一样。假如将15行的注释去掉,则创建了两次变量,list里面的两项指向不同内存。

为了将上面的问题简化,有这样一段代码,可以说明上面的几点:

test_item0,test_item1,test_item2 = [1,1],[2,2],[3,3]
test_list=[]
test_list.append(test_item0)
test_list.append(test_item1)
test_list.append(test_item2)
print "test_list = ",
print test_list
test_item0[0]=4
test_item0[1]=4
test_item1=[5,5]
print "test_list = ",
print test_list

上面代码的输出是:

test_list =  [[1, 1], [2, 2], [3, 3]]
test_list =  [[4, 4], [2, 2], [3, 3]]

可以发现,test_item0没有重新创建,而test_item1被重新创建了,从而导致test_list里面只有test_item0被更新。



本文作者: Sigma    在新浪微博关注SigmaSigmaWeibo    RSS订阅本博客
本文链接: http://www.sigma.me/2012/02/28/python-variable-and-list-append.html
本博客采用知识共享署名—非商业性-禁止演绎使用3.0协议进行许可,转载请保留作者和原文链接。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.

无觅相关文章插件,快速提升流量