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

关于Python变量和List的append操作

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

今天,写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. 2012年3月6日09:57 | #1

    “python中,只要是对字典全局赋值(右值不为字典变量),就是创建了一个字典” 。
    这句话我觉得可能有问题:
    第14行代码,等号右边是创建了一个新字典,然后把该字典的引用给等号左边。这个和直接用字典变量赋值是没有什么区别的。如果右边不是字典变量,那么这个变量就会变成一个非字典变量(Python的变量类型可以动态改变)。
    例如:
    d = {} #刚开始d为字典
    d = {‘one’:1} #d还是字典。这句话等同于d2 = dict(one=1) d=d2,或者说{‘one’:1}等同于dict(one=1),就是生成了一个字典变量并返回引用
    d = 1 #在这一步后,d就成了整数,可以查看其类型

  2. 2012年3月6日10:28 | #2

    除此之外,好像这段代码也不涉及作用域的呀。
    举例如下:
    a = 1
    print a
    for x in xrange(2):
    a = ‘hi’ #这里并不会重新生成一个局部变量a,其实还是前面那个,所以到这里以后a的类型就被改了
    print a
    print a #结果是hi

  3. 2012年3月6日10:29 | #3

    除此之外,好像这段代码也不涉及作用域的呀。
    举例如下:
    a = 1
    print a
    for x in xrange(2):
      a = ‘hi’ #这里并不会重新生成一个局部变量a,其实还是前面那个,所以到这里以后a的类型就被改了
      print a
    print a #结果是hi

    对齐没了,我再试试对齐

  4. 2012年3月6日12:55 | #4

    这句话我表述的确有问题,我本来想表达的是,右值不为字典变量的某一项

    Rex :

    “python中,只要是对字典全局赋值(右值不为字典变量),就是创建了一个字典” 。
    这句话我觉得可能有问题:
    第14行代码,等号右边是创建了一个新字典,然后把该字典的引用给等号左边。这个和直接用字典变量赋值是没有什么区别的。如果右边不是字典变量,那么这个变量就会变成一个非字典变量(Python的变量类型可以动态改变)。
    例如:
    d = {} #刚开始d为字典
    d = {‘one’:1} #d还是字典。这句话等同于d2 = dict(one=1) d=d2,或者说{‘one’:1}等同于dict(one=1),就是生成了一个字典变量并返回引用
    d = 1 #在这一步后,d就成了整数,可以查看其类型

  5. 2012年3月6日12:58 | #5

    @Rex
    这个我理解有误,LGB原则应该是在函数里面,代码块貌似没有这概念

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

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