当前位置: 首页 > 后端技术 > Python

让我们看看您是否对Python变量有很好的理解

时间:2023-03-26 19:33:51 Python

变量是编程的基本概念。Python变量看起来也很简单,但是如果理解不当,生搬硬套的话,可能会遇到一些麻烦。这里有10个代码示例来演示Python的变量特性。1.只是一个名字当a出现在赋值语句的左边时,它只代表一个名字:a=1024a='davycloud'a=['Like','Follow','Favorite']赋值完成后,this名称和右边的对象绑定在一起。在这个过程中,根本不需要考虑a是否已经绑定了其他对象,也就是说,不管a绑定了什么或者没有绑定什么,都是一个名字。只要名称是合法的,它就可以绑定到任何对象,而不管对象的类型,它表示对象的引用:a=[]b=a将a赋值给b时,a表示列表对象的引用,也就是说:列表对象的主体不会受到影响,不会被copied新列表a和b是对同一列表的引用。因为列表是可变对象,可以通过a和b的任意变量来改变对象,两者都会反映出对象的变化:a.append(1)#a=[1],b=[1]b.append(2)#a=[1,2],b=[1,2]变量对象下面再讲3.先右后左当多个赋值语句连在一起时,处理顺序是从右到左:a=b=[]其中b首先用作名称,绑定到列表对象;然后作为一个对象的引用,赋值给另一个名字a,也就是说,上面的语句等同于:#写1b=[]a=b它和下面的赋值方法有着完全不同的结果:#写2b=[]a=[]这里,两个变量分别绑定了两个不同的列表,它们之间没有关联。但是这里有个有意思的地方,如果我们把[]换成整数1或者字符串,那么写法1和写法2的两种赋值方式并没有不同的结果。4.变化取决于对象继续上面的例子,两个名字绑定到同一个list,操作其中一个,影响另一个:b=[]a=ba.append(1)这是因为该列表是一个列表。改变对象。如果列表被替换为数字或字符串:b='davy'a=ba+='cloud'a的自增操作不会影响b。先给出自增运算的等价形式:a=a+'cloud'可以看出这还是一个赋值。沿用前面的结论:右边的a代表对象的引用,即'davy',加上'cloud'生成新的对象。左边的a是一个名字,又和新的对象绑定了,即'davycloud'在一起,也就是说中间一共生成了3个字符串对象。不难看出,当我们要改变一个对象时,必须通过对象提供的接口(对于列表,就是append方法,或者下标操作,对于其他对象,可以是改变它的属性),不可能通过重新分配来更改对象。再次,赋值只是名称绑定。这里我们还可以得到另一个结论:不可变对象被多次引用/绑定,没有副作用。例如:#a,b都绑定到同一个对象a=b='davy'#a,b分别绑定到一个对象a='davy'b='davy'在上面的例子中,两个绑定的句法但是,of的含义不同,因为字符串是不可变的,即a和b即使绑定到同一个字符串,也不会相互影响。在这种情况下,何必在内存中重复创建两个相同的davy字符串。还是直接复用比较好,这样可以节省一点内存:>>>a='davy'>>>b='davy'>>>aisbTrueagain但是这个优化不是全局的,也就是andNot只要是同一个字符串,就一定是唯一的对象:>>>a+='cloud'>>>b+='cloud'>>>aisbFalse>>>a'davycloud'>>>b'davycloud'>>>a==bTrue所以,大家都知道是这样的。对于不可变对象,使用==来比较而不是is,因为它可能会产生时而正确时而错误的怪异结果。5.即时交换的秘诀当一个赋值语句右边出现多个对象,或者引用多个对象时,它们会自动打包成一个元组。赋值语句左边,需要有相同数量的名字解包:a=[1024]b='davycloud'a,b=b,a本例综合使用前三个规则:先右后左变量是对象引用的左侧是名称。不难得出结论。这里,两个名字交换了对象引用,对象体没有移动。6.都是名字的错。a没有赋值,直接运行结果:>>>print(a)Traceback(mostrecentcalllast):File"",line1,inNameError:name'a'isnotdefined这里好像很好解释,变量a没有定义!仅仅添加一行赋值语句是不够的。But:这里报的错误是NameError:name'a'isnotdefined,thenameisundefined错误,不是变量未定义仔细想想,这是什么a?数字?细绳?功能?种类?模块?7.我们都是变数。跟进一个例子:a=1defa():passclassa():passimportsysasa不仅是赋值、函数定义、类定义、模块导入或模块中的对象,而且是绑定名称。定义一个函数就是给一个函数对象绑定一个名字,定义一个类就是给一个类对象绑定一个名字,导入一个模块就是给一个模块对象绑定一个名字。Python中的一切都是对象,因此,它们都是变量。8.传参只是命名说完函数,我们继续看函数的传参:deffunc(x):returnx这个函数没什么用,只是用来说明传入传出的参数。a=func(1024)b=func(a)函数中的参数x其实是一个名字,但它的作用范围仅限于func函数内部。给函数传递参数就像是赋值,将一个对象绑定到这个内部名称,而传递参数就像是出现在赋值右边的变量,是一个对象引用:#伪代码func(1024):x=1024a=x变量的作用域就到此为止了,以后有机会再详细讨论。请注意,上述规则对于所有对象类型都是相同的,无论是可变的还是不可变的。根据前面的分析,很容易得出一个结论:如果参数/返回是不可变对象,那么就不会产生副作用。如果参数/返回是一个可变对象,那么函数内部对它的操作会影响其他绑定到这个对象上的变量,因此在向可变对象传递参数时需要格外小心。特别是,不要将可变对象用作函数默认参数。因为默认参数的绑定发生在函数定义阶段:defget_people(people=[]):returnpeople调用get_people时,除非为people指定一个参数,否则它的绑定总是在函数定义那一刻生成的。而我们的初衷可能是如果默认没有参数,就会生成一个空列表。通常的做法是在函数内创建一个新对象:defget_people(people=None):如果people为None:people=[]returnpeople9.不能删除的对象Python提供了del关键字来删除变量,然而实际上这个操作的结果只是解除变量名与对象的绑定,然后删除名称。如果再次访问已删除的名称,将触发NameError,就好像它从未存在过一样。至于对象,只是减少一个引用:a=[1,2,3]b=adela#完全不会影响b。当一个对象引用了多个变量时,它不会被清理。这很容易理解。事实上,即使当前没有名称绑定到这个对象,这个对象也不会被立即删除:>>>a=[]>>>id(a)2292904305736>>>dela>>>aTraceback(最近一次调用last):File"",line1,inNameError:name'a'isnotdefined>>>b=[]>>>id(b)2292904305736总之,del的意思是解除绑定,不要指望它删除对象。对象的引用计数和销毁都是Python内部维护的,一般情况下我们不需要关心。有兴趣的可以参考Python垃圾回收的相关内容。10.浅拷贝?零拷贝!终极例子:a=[[]]*3a[0].append(1)print(a)#[[1],[1],[1]]这个比较混乱,因为用到了列表的*操作.列表相乘时,一般的理解就是复制其中的元素。看到copy很容易提到所谓的浅拷贝和深拷贝。这明显不是深拷贝,所以很容易想当然的认为是浅拷贝,错了!这只是另一个不可见的赋值,让我们展开它:x=[]y=[x]a=[x,x,x]#相当于a=y*3a[0].append(1)最重要的关于print(a)的事情是第三行代码:y*3是将y中的元素复制3份。现在这个元素是x,所以让x出现3次。实际效果是[x,x,x],x是对象的引用,所以我们只是复制了3次对象的引用,对象的body根本没有动。那么我要真正复制这个列表怎么办呢?使用系统提供的浅拷贝功能,或者使用切片:#列表内置的拷贝方法a=[x.copy()foriinrange(3)]#使用切片a=[x[:]foriinrange(3)]#使用复制标准库fromcopyimportcopya=[copy(x)foriinrange(3)]前两个方法是list对象的接口,而copy模块是更一般。总结变量是指绑定到对象的名称。当对象被绑定时,变量就是名称。使用时,变量表示对象的引用。只有绑定关系发生变化。如果要更改/复制对象,则需要查看对象是否提供方法。除了以上内容,关于变量还有一个重要的概念就是了解它的作用域,这将在另一篇文章中解释。如果本文对您有帮助,请点赞、分享、关注,谢谢!