解析Python中的变量、引用、拷贝和作用域的问题

 更新时间:2015年04月07日 11:41:23   投稿:goldensun  
这篇文章主要介绍了Python中的变量、引用、拷贝和作用域的相关问题,是Python学习过程当中必会的基础知识,需要的朋友可以参考下

在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样。在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可。但是,当用变量的时候,必须要给这个变量赋值;如果只写一个变量,而没有赋值,那么Python认为这个变量没有定义。如下:
 

>>> a
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

    下面我们具体讲一下Python中的变量,引用,拷贝和作用域问题。。

    一、可变对象 & 不可变对象

    在Python中,对象分为两种:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。需要注意的是:这里说的不可变指的是值的不可变。对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。

    下面是一些例子:
 

>>> a = 'xianglong.me'
>>> id(a)
140443303134352
>>> a = '1saying.com'
>>> id(a)
140443303131776
# 重新赋值之后,变量a的内存地址已经变了
# 'xianglong.me'是str类型,不可变,所以赋值操作知识重新创建了str '1saying.com'对象,然后将变量a指向了它

 

>>> a_list = [1, 2, 3]
>>> id(a_list)
140443302951680
>>> a_list.append(4)
>>> id(a_list)
140443302951680
# list重新赋值之后,变量a_list的内存地址并未改变
# [1, 2, 3]是可变的,append操作只是改变了其value,变量a_list指向没有变

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

201547113851606.jpg (668×316)

    三、函数值传递

    先看一个例子:
 

def func_int(a):
  a += 4
 
def func_list(a_list):
  a_list[0] = 4
 
t = 0
func_int(t)
print t
# output: 0
 
t_list = [1, 2, 3]
func_list(t_list)
print t_list
# output: [4, 2, 3]

    对于上面的输出,不少Python初学者都比较疑惑:第一个例子看起来像是传值,而第二个例子确实传引用。其实,解释这个问题也非常容易,主要是因为可变对象和不可变对象的原因:对于可变对象,对象的操作不会重建对象,而对于不可变对象,每一次操作就重建新的对象。

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

    四、浅拷贝 & 深拷贝

    接下来的问题是:如果我们一定要复制一个可变对象的副本怎么办?简单的赋值已经证明是不可行的,所以Python提供了copy模块,专门用于复制可变对象。copy中有两个方法:copy()和deepcopy(),前一个是浅拷贝,后一个是深拷贝。浅拷贝仅仅复制了第一个传给它的对象,下面的不管了;而深拷贝则将所有能复制的对象都复制了。下面是一个例子:
 

a = [[1, 2, 3], [4, 5, 6]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
 
a.append(15)
a[1][2] = 10
 
print a
print b
print c
print d
 
# [[1, 2, 3], [4, 5, 10], 15]
# [[1, 2, 3], [4, 5, 10], 15]
# [[1, 2, 3], [4, 5, 10]]
# [[1, 2, 3], [4, 5, 6]]

    五、作用域

    在Python程序中创建、改变或查找变量名时,都是在一个保存变量名的地方进行中,那个地方我们称之为命名空间。作用域这个术语也称之为命名空间。具体地说,在代码中变量名被赋值(Python中变量声明即赋值,global 声明的只是变量的使用域)的位置决定了该变量能被访问的范围。函数定义了本地作用域,而模块定义的是全局作用域。

    每一个模块都是全局作用域。也就是说,创建于模块文件顶层的变量具有全局作用域,对于外部访问就成了一个模块对象的属性。全局作用域的作用范围仅限于单个文件。“全局”指的是在一个文件的顶层变量名对于这个文件而言是全局的。每次对函数的调用都创建了一个新的本地作用域。Python中也有递归,即可以调用自身,每次调用都会创建五个新的本地命名空间。赋值的变量名除非声明为全局变量,否则均为本地变量。如果需要在函数内部对模块文件顶层的变量名赋值,需要在函数内部通过 global 语句声明该变量。所有的变量可归纳为本地、全局或者内置三种。范围分别为def内部,在一个模块的命名空间内部和预定义的 __builtin__ 模块提供的变量。

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

    当在函数中使用未确定的变量名时,Python搜索4个作用域:本地作用域(L),之后是上一层嵌套结构中 def 或 lambda 的本地作用域(E),之后是全局作用域(G),最后是内置作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,Python 会报错的。下图说明了搜索流程(由内及外):

    上面说了,Python中的变量是没有类型的,但Python其实是区分类型的:Python的所有变量其实都是指向内存中的对象的一个指针,都是值的引用,而其类型是跟着对象走的。总结来说:在Python中,类型是属于对象的,而不是变量, 变量和对象是分离的,对象是内存中储存数据的实体,变量则是指向对象的指针。在《Learning Python》一书中有一个观点:变量无类型,对象有类型,大概也是说的这个意思。下面是一张说明变量的图:
    

201547113948152.png (417×321)

    Python像PHP一样提供了一个global语法,global定义的本地变量会变成其对应全局变量的一个别名,即是同一个变量。下面的例子可以帮你更好的理解:
 

a = 44
 
def test1():
  a = 14
  print a
test1() # 输出:14
 
def test2():
  global a
  print a
test2() # 输出:44

相关文章

  • pycharm 如何缩进和SQL乱码及SQL包含变量

    pycharm 如何缩进和SQL乱码及SQL包含变量

    这篇文章主要介绍了pycharm 如何缩进和SQL乱码及SQL包含变量,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • flask 实现上传图片并缩放作为头像的例子

    flask 实现上传图片并缩放作为头像的例子

    今天小编就为大家分享一篇flask 实现上传图片并缩放作为头像的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • Python实现语音识别Whisper的使用示例

    Python实现语音识别Whisper的使用示例

    Whisper是由OpenAI基于Python开发的能够识别多国语言的语音识别模型,本文主要介绍了Python实现语音识别Whisper的使用示例,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • QML使用Python的函数过程解析

    QML使用Python的函数过程解析

    这篇文章主要介绍了QML使用Python的函数过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • python 初始化一个定长的数组实例

    python 初始化一个定长的数组实例

    今天小编就为大家分享一篇python 初始化一个定长的数组实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • Python类的继承super相关原理解析

    Python类的继承super相关原理解析

    这篇文章主要介绍了Python类的继承super相关原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Python机器学习应用之基于LightGBM的分类预测篇解读

    Python机器学习应用之基于LightGBM的分类预测篇解读

    这篇文章我们继续学习一下GBDT模型的另一个进化版本:LightGBM,LigthGBM是boosting集合模型中的新进成员,由微软提供,它和XGBoost一样是对GBDT的高效实现,原理上它和GBDT及XGBoost类似,都采用损失函数的负梯度作为当前决策树的残差近似值,去拟合新的决策树
    2022-01-01
  • 使用python的pandas库读取csv文件保存至mysql数据库

    使用python的pandas库读取csv文件保存至mysql数据库

    这篇文章主要介绍了利用python的pandas库读取csv文件保存至mysql数据库的方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • pygame实现贪吃蛇小游戏

    pygame实现贪吃蛇小游戏

    这篇文章主要为大家详细介绍了pygame实现贪吃蛇小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 通过Python实现自动填写调查问卷

    通过Python实现自动填写调查问卷

    这篇文章主要介绍了通过Python实现自动填写调查问卷的相关资料,需要的朋友可以参考下
    2017-09-09

最新评论