Python 中那些使人防不胜防的坑(一)

在这里插入图片描述

你们好,我是 Rocky0429,一个正在学习 Python 的蒟蒻…web


在学习 Python 的过程当中,我为它的简洁优雅而痴迷,但它又是如此的调皮,在提供了不少舒服的功能特性以外,又悄悄挖了不少带有迷惑性的坑,使人防不胜防…算法


人不能两次踏入同一条河流,在无数次踩进一样的坑里以后,我以为我有必要整理一下,一为自警,二为给你们提个醒,但愿你不要和我犯相同的错误。编程


这会是一个系列,每篇 5 个,系列文章更新不定,不想错过的,记得点个关注,不迷路。微信



0x00 走丢的外部变量


首先咱们先来看这么一个例子:数据结构


e = 429

try:
   raise Exception()
except Exception as e:
   pass

print(e)

PS:except Exception as e 能够捕获除了与程序退出(sys.exit())相关以外的全部异常。编程语言


在继续向下看以前,你能够先思考一下上述例子可能出现的结果是什么,也能够本身尝试着在编译器里输入一下。思考完了请继续往下看。svg


出现的结果以下:函数


Traceback (most recent call last):
 File "test.py", line 8, in <module>
   print(e)
NameError: name 'e' is not defined

居然报错了,那么这究竟是为何呢?

其实这是由于在 Python3 中使用 as 分配异常的时候,在 except 的末尾将会把这个异常清除掉(在 Python2 中则不会出现这样的状况)。这就好像将上面的示例变成下面的样子:学习


e = 429

try:
   raise Exception()
except Exception as e:
   try:
       pass
   finally:
       del e

print(e)

经过上面的变形代码,咱们能够很清楚的看明白发生这一切的根源是什么:由于 e 被删除了。这也变相的告诉咱们,若是想要在 except 后面引用 e,必须将它先赋值给其它变量。

这样看来,是变量 e 执行了 except 子句而被删除,可是为何 e 会去执行 except 子句呢?仅仅是由于 e 和 as 后面的 e 长的一毛同样?code


答案是否认的,其实这个是由于子句在 Python 中没有独立的做用域,因此上述示例中的全部内容都处于同一个做用域里,因此变量 e 会由于执行了 except 子句而被删除。



0x01 一样是加,却不必定等价


在咱们来表示「加」这个概念的时候,通常咱们会用两种方式:a = a + b或者是 a += b 。在不少人的概念里这两个其实就是一种,不分彼此,好比以前我就是这么认为的,直到有一天有人拿着下面的坑过来让我踩...

首先咱们先来看第一个例子:


>>> a = [1,2,3]
>>> b = a
>>> a = a + [4,5,6]

一个很简单的例子,你知道此时的 a 和 b 分别是多少么?请先本身思考一下再继续向下看:


>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3]

估计不少人都会答对,表达式 a = a + [4,5,6] ,等号右边实际上是造成了一个新的列表,最后让 a 引用了这个新的列表,而 b = a 引用的是以前的 a,因此 b 保持不变。

明白了上面的例子,咱们接下来再看一个稍微有点区别的例子:


>>> a = [1,2,3]
>>> b = a
>>> a += [4,5,6]

上面的例子和文章开头的例子区别在从 + 变成了 +=,按照咱们惯性思惟去想,确定觉得这俩例子就是一个东西的两种不一样写法而已,可实际上真的是这样吗?让咱们来看一下此时的 a,b:
>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3, 4, 5, 6]

咦?一样是印象里的「加」,好像真的哪里有点不同诶。。

经过上面咱们就能够看出 a = a + b 和 a += b 并不老是表现相同,起码在列表上就是这么表现的。在这里的 a += [4,5,6] 实际上使用的是 extend 函数,因此 a 和 b 仍然指向已被修改的同一列表。


既然在这里说到了 + 和 +=,索性再多补充一点:在使用「加」的概念来链接字符串的时候,+= 其实比 + 的速度更快。


下面咱们来实际的演示一下用 + 链接三个字符串:


>>> import timeit
>>> timeit.timeit("a = a + b + c", setup="a='a'*10000;b='b'*10000;c='c'*10000",number=100)
0.07921688999340404
>>> timeit.timeit("a += b + c", setup="a='a'*10000;b='b'*10000;c='c'*10000",number=100)
0.002059974998701364

上面的两个结果很容易看出来,在处理速度上,+= 比 + 处理的速度要快的多。出现这样现象的缘由是 += 执行的是追加操做,追加操做的话就会比 + 少了一个销毁而后新建的动做,好比在 a += b + c 上 a 就不会被销毁。

0x02 不通常的小括号


不少学过别的编程语言的同窗,很容易会忽略小括号 “()” 在 Python 中的一个重要表现,那就是小括号还能表示“元组” 这一不可变数据类型。


>>> type(())
<class 'tuple'>
>>> tur = (1, 2)
>>> type(tur)
<class 'tuple'>

可是若是小括号内只有一个元素的话,好比像下面这样,它就是小括号内的元素类型:
>>> tur = (1)
>>> type(tur)
<class 'int'>

天呐个天...

那么若是想要表示一个元素的元组咋整呢?要像下面这样:


>>> tur = (1, )
>>> type(tur)
<class 'tuple'>

加个逗号 “,” 就 ok 啦...

0x03 列表的删除没有那么简单


假如咱们有一个列表,我想删除列表中的元素:
>>> lst = [1, 2, 3, 4, 5]
>>> for i in lst:
... lst.remove(i)

在刚开始学习 Python 之初,这是不少同窗容易想到的作法,然而咱们来看一下程序运行完之后的结果:
>>> lst
[2, 4]

看到这个结果,我...

在这里插入图片描述


这是为啥子呢?是由于在 for 循环中,若是咱们删除了 index = 0 (即 1)的值,本来 index = 1 及其以后的值会向前补位,因此当前 index = 1 的值为以前 index = 2 的值。


列表的删除操做咱们常常要用,因此你们要打起十二分的精神来对它。



0x04 is not 不分家


is not 在 Python 中是一伙的,用的时候要靠在一块儿,分开之后就是两个东西,结果会不同…


>>> [1, 2, 3] is not None
True
>>> [1, 2, 3] is (not None)
False

这就在于,is not 是个单独的二元运算符,当它两侧的变量指向同一个对象的时候,结果为 False,不然结果为 True,但愿引发你们的注意...

❤️ 看完有所收获?但愿爱学习的你不要吝啬三连击哟[点赞 + 收藏 + 评论]~


❤️能够关注个人原创公众号:「Python空间」,更多优质的技术文章第一时间更新。最后送你新人大礼包一份,关注微信公众号,后台回复:“CSDN” 便可获取!

做者Info:

【做者】:Rocky0429 【原创公众号】:Python空间。 【简介】:CSDN 博客专家, 985 计算机在读研究生,ACM 退役狗 & 亚洲区域赛银奖划水选手。这是一个坚持原创的技术公众号,专一Python 编程,天天坚持推送各类 Python 基础/进阶文章,数据分析,爬虫实战,数据结构与算法,不按期分享各种资源。 【转载说明】:转载请说明出处,谢谢合做!~