Python中的变量、引用、拷贝和作用域

来源:互联网 发布:js 画线条 编辑:程序博客网 时间:2024/05/01 05:09
在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样。在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可。但是,当用变量的时候,必须要给这个变量赋值;如果只写一个变量,而没有赋值,那么Python认为这个变量没有定义。如下:



一、python中的变量与对象、可变对象和不可变对象:

a = 3         #创建 int对象 3,创建变量a, 变量a指向对象3
a = "test"    #创建string对象,变量a指向对象"test"

对象:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。

需要注意的是:这里说的不可变指的是对象值的不可变。

(1)、对于不可变类型的对象,如果要更改变量,则会创建一个新值,把变量绑定到新的对象上,而旧值如果没有被引用就等待垃圾回收。

(2)、可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。


举例说明:
        
# 重新赋值之后,变量a的内存地址已经变了
# 'hello'是str类型,不可变,所以赋值操作重新创建了str 对象 'world'对象,然后将变量a指向了它

        

# list重新赋值之后,变量lst的内存地址并未改变

# [1, 2, 3]是可变的,append操作只是改变了其value,变量lst指向没有变


二、变量无类型,对象有类型

总结来说:在Python中,类型是属于对象的,而不是变量, 变量和对象是分离的,对象是内存中储存数据的实体,变量则是指向对象的指针。    

     
三、python引用

python一般内部赋值变量的话,都是传个引用变量,和C语言的传地址的概念差不多,

比如:
a = [1,2,3]               表示变量a保存了这个列表的地址
python里可以用id()来查询下              a在内存的地址是:675375852


b = a 

那b的内容是什么,地址又是什么呢?
用print 输出下b的内容也是[1,2,3]
然后我们查看下b的地址看下能否验证我们的结论
print id(b)
果然b的地址也是:675375852


这样会带来一个问题,因为变量a,和变量b都是保存了同一个列表的地址。如果我改变a指向的列表的值的话,那b指向的列表的值也同时改变
比如:     a[1] = 6
print a
输出的内容是[1,6,3]
print b
b指向的列表的内容也是[1,6,3]

如果我们只想修改a列表里面的内容。而不想修改b的内容,那就要用到python的拷贝了
a=[1,2,3]
b=a[:]    ###拷贝了一份a的内容给b
a[1]=6
print a
输出a的内容是[1,6,3]
而b的内容不是[1,6,3]
而是[1,2,3]


四、函数值传递

先看一个例子:
def func_int(a):    a += 4def func_list(lst):    lst[0] = 4    t = 0func_int(t)print t# output: 0t_list = [1, 2, 3]func_list(t_list)print t_list# output: [4, 2, 3]
输出结果:


主要是因为可变对象和不可变对象的原因:对于可变对象,对象的操作不会重建对象,而对于不可变对象,每一次操作就重建新的对象。

在函数参数传递的时候,Python其实就是把参数里传入的变量对应的对象的引用依次赋值给对应的函数内部变量。参照上面的例子来说明更容易理解,func_int中的局部变量"a"其实是全部变量"t"所指向对象的另一个引用,由于整数对象是不可变的,所以当func_int对变量"a"进行修改的时候,实际上是将局部变量"a"指向到了整数对象"1"。所以很明显,func_list修改的是一个可变的对象,局部变量"a"和全局变量"t_list"指向的还是同一个对象。


五、浅拷贝 & 深拷贝

接下来的问题是:如果我们一定要复制一个可变对象的副本怎么办?简单的赋值已经证明是不可行的,所以Python提供了copy模块,专门用于复制可变对象。


copy中有两个方法:copy()和deepcopy(),前一个是浅拷贝,后一个是深拷贝

浅拷贝仅仅复制了第一个传给它的对象,下面的不管了;而深拷贝则将所有能复制的对象都复制了。


下面是一个例子:
import copya = [[1, 2, 3], [4, 5, 6]]b = ac = copy.copy(a)d = copy.deepcopy(a)a.append(15)a[1][2] = 10print aprint bprint cprint d

输出结果:



六、作用域

     在Python程序中创建、改变或查找变量名时,都是在一个保存变量名的地方进行中,那个地方我们称之为命名空间。作用域这个术语也称之为命名空间。

    变量名引用分为三个作用域进行查找:首先是本地,然后是函数内(如果有的话),之后是全局,最后是内置。在默认情况下,变量名赋值会创建或者改变本地变量。全局声明将会给映射到模块文件内部的作用域的变量名赋值。Python 的变量名解析机制也称为 LEGB 法则,具体如下:

    当在函数中使用未确定的变量名时,Python搜索4个作用域:本地作用域(L),之后是上一层嵌套结构中 def 或 lambda 的本地作用域(E),之后是全局作用域(G),最后是内置作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,Python 会报错的。
             
参考博文:  https://my.oschina.net/leejun2005/blog/145911
0 0
原创粉丝点击