一、错误地使用replace函数
【错误用法和出现的情形】
#当我们对一个序列进行操作时,有时会遇到要删去一个序列中的某一段,如对于下面的字符串string和列表lis string = '123' lis = [1,2,3] #列表我们可以用remove的方法对其进行操作(或者del) lis.remove(2) #再看到字符串,del string[1] 可以吗? #显然不行,这会报错! #于是我们就想到用字符串的replace方法 #然后!就有小伙伴自以为是地以为replace方法与remove方法类似,都是对原序列进行操作! string.replace('2','') #然而!执行上述语句后,lis变成了[1,2],而string还是'123'!(这里不会报错)
【正确做法】
1. string = '123' 2. string = string.replace('2','') 3. 4. #此时string的结果为'13'
二、错误地创建多维列表
【错误用法和出现的情形】
#在创建长度较长的一维列表时,使用乘法创建是个不错的选择 lis = [0,0,0,0,0,0,0,0,0,0] #一般创建方式 lis = [0]*10 #乘法创建方式 #显然,这样是没有任何问题的,而且对于非常长的列表,乘法创建会使列表看起来更加优雅 #但是!对于创建多维列表时,这样做是存在重大问题的! #一般人,会这样创建二维列表 lis1 = [[0,0,0,0,0],[0,0,0,0,0]] #但就有的小伙伴投机取巧 lis2 = [[0]*5]*2 #然而!这个二维列表lis表面上是[[0,0,0,0,0],[0,0,0,0,0]],和一般创建没有区别,但其实问题巨大! lis1[1][1] = 1 lis2[1][1] = 1 #按道理,这两个列表的修改后的结果应该一样,但实际上 ''' >>>lis1 >>>[[0, 0, 0, 0, 0], [0, 1, 0, 0, 0]] >>>lis2 >>>[[0, 1, 0, 0, 0], [0, 1, 0, 0, 0]] ''' #他俩长得一点儿都不一样!
【正确做法】
1. #这是由于嵌套列表用乘法创建时,内层的小列表只是引用到同一个地方而已,并不会开辟一个新的内存空间 2. 3. lis = [[0]*5 for _ in range(2)]
三、错误地修改变量的值
【错误用法和出现的情形】
#当我们要对字符串进行合并操作时,我们会这样 a = '123' b = '456' a += b #运用加法赋值运算符,可以使a变成'123456' #显然,这没有任何问题! #但是!当有很多变量要进行相同的操作时,就有小伙伴自作聪明 a,b,c,d = [1,2,3,4] for i in [a,b,c,d]: i = i*4+5/3 #看上去没有任何问题,其实问题大大! #此时将a,b,c,d打印出来 ''' >>>a,b,c,d >>>1,2,3,4 ''' #没有任何变化!
【正确做法】
#这是由于for循环只是修改了列表里的值而已,而列表只是引用了a,b,c,d的值,并不是引用这几个变量(相当于修改临时值) a,b,c,d = [1,2,3,4] lis = [a,b,c,d] for i in range(len(lis)): lis[i] = i*4+5/3 a,b,c,d = lis
四,错误地调用全局变量
【错误用法和出现的情形】
#当我们在函数中调用函数外部的局部变量时,容易被这几个情况弄得稀里糊涂 #情形一 counter = 0 def access(): counter += 1 print(counter) access() #情形二 counter = 0 def access(): print(counter) access() #在这里,情形一会报错!(counter没有被定义)而情形二不会!
【正确做法】
#在局部变量不存在的情况下会自动调用全局变量,这是对的(关键在于调用) #但是,这里我们忽略了一个小细节, #“+=”是“加法赋值运算符”,在Python中属于赋值运算符中的一种 #也就是说,“counter += 1”是给变量counter赋值,但它并没有调用全局变量counter(等号后的才会被调用) #要加法赋值,必须要先有变量才行,自然会报错,且报错内容是 #local variable 'counter' referenced before assignment(局部变量“counter”在赋值前引用) #我们用关键字对counter变量进行声明,声明其为全局变量 counter = 0 def access(): global counter counter += 1 print(counter) access() #下面是关键字global的代码解释 def func1(): global value value = 1 func1() def func2(): print(value) func2()
五、错误地使用lambda匿名函数
【错误用法和出现的情形】
#我们有时候要多个有一定规律的函数运行后产生结果时,一般人会这样 def func1(t):print((1,t)) def func2(t):print((2,t)) def func3(t):print((3,t)) def func4(t):print((4,t)) def func5(t):print((5,t)) for func in [func1,func2,func3,func4,func5]: func(1) #这样输出的结果为 ''' (1, 1) (2, 1) (3, 1) (4, 1) (5, 1) ''' #但是!总有小伙伴投机取巧 lis = [lambda t:(v0,t) for v0 in [1,2,3,4,5]] for i in lis:print(i(1)) #这种错误可能在我们遇到闭包时会经常出现 def func(): return [lambda t:(v0,t) for v0 in [1,2,3,4,5]] for f in func(): print(f(1)) #以上两段代码的输出均为 ''' (5, 1) (5, 1) (5, 1) (5, 1) (5, 1) ''' #v0取的值都是最后一个,然而这并不是我们预期的结果! #这在Tkinter界面化编程中也有体现,比如在使用多个重复但位置不同的Button控件
【正确做法】
#之前的代码中,v0保留的是最近的值 #但是!如果我们将v0声明为关键字参数,问题就解决了 #因为一旦函数被定义,其关键字参数的值是不变的 lis = [lambda t,v0=v0:(v0,t) for v0 in [1,2,3,4,5]] for i in lis:print(i(1))
六、错误地拷贝嵌套列表
【额外解决方法】
#这个想必我们大家都比较熟悉了吧? #对于一维列表,我们可以用一下三种方式进行拷贝 lis = [1,2,3] lis1 = lis[:] lis2 = lis.copy()#内置函数 from copy import * lis3 = copy(lis)#copy模块里的函数 #对于二维嵌套列表,上述的三种方法全部失效! #现在我们所知道的解决办法是 from copy import copy lis = [[1,2],[3,4]] lis4 = deepcopy(lis) #但是这里我想介绍一种不用引入copy模块中的deepcopy函数就可以解决的办法 #注意:Python内置函数中有copy,但没有deepcopy lis = [[1,2],[3,4]] lis5 = eval(str(lis)) #注意:嵌套列表的拷贝与上面讲到的嵌套列表的乘法创建不同,下面的方法依然是浅拷贝 lis6 = [i for i in lis]
七、对函数的默认参数认知有误
【错误用法和出现的情形】
#大家对函数的默认参数应该了解不少吧? #下面的做法或许我们经常会用到 def function(argument,default_argument=[]): default_argument.append(argument) print(default_argument) function(1) #输出为[1],这看似没什么毛病,也确实没什么毛病 #其实它有没有毛病就取决于你要怎么用这个函数 #当我们要重复使用这个函数时,就有问题了 def function(argument,default_argument=[]): default_argument.append(argument) print(default_argument) function(1) function(4,default_argument=[2,3]) function(5) #你认为上面的输出结果是什么呢? #一般人就会认为是: ''' [1] [2, 3, 4] [5] ''' #想必你也是这样想的吧? #如果你这样想就大错特错了!!! #真实的输出是这样的: ''' [1] [2, 3, 4] [1, 5] '''
【正确做法】
#其实这是我们对函数的默认参数理解错误导致的 #默认参数的意思是,如果没有传递参数,那么函数就会取这个参数的默认值,这一点没有问题 #但关键就在于这个默认值是什么!不会有人以为是等号后面的值吧? #如果认为是等号的值,那就错了! #等号后面的是默认参数的初始值!并不是默认值! #其实Python中的函数也是一个对象,在第一次执行函数时就会对其进行初始化,而它也是有一些属性和方法的 #这个默认值就是它的属性之一,随着函数的多次使用,它在内存的值会慢慢改变 #而函数每次取其默认值是从内存中取的,而不是重新将默认参数赋值为等号后面的值! def function(argument,default_argument=[]): default_argument.append(argument) return default_argument#将print改为return for i in range(10): function(i) print(function(10)) #输出结果为[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] #若我们不想要这样的结果,而是要该函数每次执行后默认参数就是空列表("[]")的话,应该这样 def function(argument,default_argument=[]): default_argument.append(argument) return default_argument#将print改为return for i in range(10): function(i,[]) print(function(10,[])) #输出结果为[10]
八、错误地修改列表
【错误用法和出现的情形】
# 我们在修改列表时,可能会犯这样的错误,它咋一看没啥问题,仔细分析问题可大了 lis = [1, 2, 3, 4, 5] # 现在我们想删除列表里的某个值或全部值 # 简单的情况下,我们肯定不会犯错 lis.clear() # 清空列表 del lis[2] # 删除列表第3个值 lis.remove(3) # 删去3这个值 # 是不是感觉上面的没有什么问题,对吧 # 上面的很简单,所以就没有什么错误容易犯,但是,你看下面这个 for i in lis: lis.remove(i) # 有什么问题? # lis只能删除一半的值!
【正确做法】
# 这是因为我们在删除的过程中,lis也在随之发生变化,也就是说,lis在迭代过程中发生了变化! # 这是由于列表的引用特性导致的,元组不会这样,字典会给你报错 # 我们都知道,列表的赋值是引用,而并非真的拷贝了一份数据 # 所以迭代时,for循环迭代的就是原来的列表,列表被修改,长度减少,for循环自然会提前结束 # 于是就产生了只删去一半的结果 # 下面是正确做法 for i in tuple(lis): lis.remove(i) # 或者这样子 from copy import deepcopy for i in deepcopy(lis): lis.remove(i)