Python 易错点大集合

简介: Python 易错点大集合

一、错误地使用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)
目录
相关文章
|
24天前
|
安全 网络安全 文件存储
思科设备巡检命令Python脚本大集合
【10月更文挑战第18天】
62 1
思科设备巡检命令Python脚本大集合
|
1月前
|
存储 缓存 API
解密 Python 集合的实现原理
解密 Python 集合的实现原理
37 11
|
1月前
|
存储 自然语言处理 数据处理
使用Python计算多个集合的交集详解
使用Python计算多个集合的交集详解
38 1
|
2月前
|
存储 API 索引
Python 的集合是怎么实现的?
Python 的集合是怎么实现的?
48 9
|
2月前
|
存储 索引 Python
Python常用数据结构——集合
Python常用数据结构——集合
|
2月前
|
存储 数据处理 Python
Python中的Set集合:高效数据处理的利器
Python中的Set集合:高效数据处理的利器
40 0
|
2月前
|
Python
python推导式-列表,元组,字典,集合推导式
这篇文章介绍了Python中的推导式,包括列表推导式、元组推导式、字典推导式和集合推导式,提供了它们的基本格式和示例代码,并解释了推导式如何简化循环和条件判断的代码编写。
|
3月前
|
数据采集 编解码 算法
Github | 推荐一个Python脚本集合项目
Github | 推荐一个Python脚本集合项目
|
3月前
|
索引 Python 容器
为什么Python中会有集合set类型?
为什么Python中会有集合set类型?
|
3月前
|
存储 索引 Python
五:《Python基础语法汇总》— 列表&元组&集合
本篇文章讲解了关于列表;元组和集合这三个基本数据类型的常用方法与函数。及同一性操作符;成员判断符;浅拷贝与深拷贝等多方面的知识点
37 4