python老是报错,学习python经常会碰到什么错误

坚强的del

classSomeClass:def__del__(self):print("Deleted!")x=SomeClass()y=xdelxdely#输出:Deleted!

你发现了几个问题?第一、一个变量删除了两次竟然没有报错。第二、执行了两次删除只有一次打印了删除操作。修改一下上面的代码

00ce6ac52646fa359962a45f16b20b78.png

x=SomeClass()y=xprint(dir())#输出:['SomeClass','__annotations__','__builtins__','__cached__','__doc__','__file__','__loader__','__name__','__package__','__spec__','x','y']delxprint(y)#输出:print(dir())#输出:delyprint(dir())Deleted!['SomeClass','__annotations__','__builtins__','__cached__','__doc__','__file__','__loader__','__name__','__package__','__spec__']

可以看到x、y是两个变量,但是他们指向了同一个对象,Python使用引用计数进行内存管理,所以当x=SomeClass()的时候,对象上的指针引用计数从0变1,y=x的时候,引用计数加1变成2.

delx并不会立刻调用x.__del__().

每当遇到delx,Python会将对象的引用数减1,当对象的引用计数减到0时才会真正的删除对象,因此调用x.__del__().

迭代列表时删除元素

list_1=[1,2,3,4]list_2=[1,2,3,4]list_3=[1,2,3,4]list_4=[1,2,3,4]foridx,iteminenumerate(list_1):delitemforidx,iteminenumerate(list_2):list_2.remove(item)foridx,iteminenumerate(list_3[:]):list_3.remove(item)foridx,iteminenumerate(list_4):list_4.pop(idx)print(list_1)#输出:[1,2,3,4]print(list_2)#输出:[2,4]print(list_3)#输出:[]print(list_4)#输出:[2,4]

我们先看一下del,remove和pop的不同:

delvar_name只是从本地或全局命名空间中删除了var_name(这就是为什么list_1没有受到影响).

remove会删除第一个匹配到的指定值,而不是特定的索引,如果找不到值则抛出ValueError异常.

pop则会删除指定索引处的元素并返回它,如果指定了无效的索引则抛出IndexError异常.

list_2/list_4为什么输出[2,4]

列表迭代是按索引进行的,所以当我们从list_2或list_4中删除1时,列表的内容就变成了[2,3,4].剩余元素会依次位移,也就是说,2的索引会变为0,3会变为1.由于下一次迭代将获取索引为1的元素(即3),因此2将被彻底的跳过.类似的情况会交替发生在列表中的每个元素上.

list_3为什么会输出[]

这个好像比较符合我们的预期值,这里写法有些不一样,我们看一看下面代码

a=[1,2,3,4]print(id(a))#输出:4523069920print(id(a[:]))#输出:4523072480

看出来问题了吗?切片操作会创建一个新对象,所以不存在上面的问题

循环变量泄漏!

forxinrange(7):ifx==6:print(x,':forxinsideloop')print(x,':xinglobal')#输出:6:forxinsideloop#输出:6:xinglobal

在Python中,for循环使用所在作用域并在结束后保留定义的循环变量.如果我们曾在全局命名空间中定义过循环变量.在这种情况下,它会重新绑定现有变量。但是要注意列表推导式里的局部变量是不能在外部使用的。

print([xforxinrange(5)])#输出:[0,1,2,3,4]print(x,':xinglobal')#输出:#Traceback(mostrecentcalllast):#NameError:name'x'isnotdefined

当心默认的可变参数!

defsome_func(default_arg=[]):default_arg.append("some_string")returndefault_argprint(some_func())#输出:['some_string']print(some_func())#输出:['some_string','some_string']print(some_func())#输出:['some_string','some_string','some_string']print(some_func())#输出:['some_string','some_string','some_string','some_string']

这里必须要敲黑板、敲黑板、敲黑板,在很多编程语言中函数都有默认参数,但是Python中默认参数不一样,因为python中默认参数是存储在一个独立的区域,当函数被定义的时候,默认参数被创建,直到程序终止。当我们默认参数为不可变对象时,与其他语言类似。但是如果默认参数为不可变对象时,每一次的变化就会被记住,这种问题非常严重,经常发生问题的时候我们找不到问题点。所以我们建议大家一定不要把可变对象设置为默认参数,可以使用如下方式进行修改:

defsome_func(default_arg=None):ifnotdefault_arg:default_arg=[]default_arg.append("some_string")returndefault_argprint(some_func())#输出:['some_string']print(some_func())#输出:['some_string']

同人不同命!

a=[1,2,3,4]b=aa=a+[5,6,7,8]print(a)#输出:[1,2,3,4,5,6,7,8]print(b)#输出:[1,2,3,4]

这里牵扯到python中赋值运算符的本质问题,后面直播或者出视频来解释一下,一定要记住:赋值运算符等同于创建新对象。这一点也很重要,主要是针对定位问题。

a+=b并不总是与a=a+b表现相同.类实现op=运算符的方式也许是不同的,列表就是这样做的.

表达式a=a+[5,6,7,8]会生成一个新列表,并让a引用这个新列表,同时保持b不变.

表达式a+=[5,6,7,8]实际上是使用的是"extend"函数,所以a和b仍然指向已被修改的同一列表.

外部作用域变量

a=1defsome_func():returnadefanother_func():a+=1returnaprint(some_func())#输出:1print(another_func())#输出:#Traceback(mostrecentcalllast):#another_func()#a+=1#UnboundLocalError:localvariable'a'referencedbeforeassignment

当你在作用域中对变量进行赋值时,变量会变成该作用域内的局部变量.因此a会变成another_func函数作用域中的局部变量,但它在函数作用域中并没有被初始化,所以会引发错误.

可以阅读这个简短却很棒的指南,了解更多关于Python中命名空间和作用域的工作原理.

想要在another_func中修改外部作用域变量a的话,可以使用global关键字

defanothre_func():globalaa+=1returna

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值