前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 中那些令人防不胜防的坑(一)

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

作者头像
编程文青李狗蛋
发布2019-11-21 20:16:12
4100
发布2019-11-21 20:16:12
举报

0x00 走丢的外部变量

首先我们先来看这么一个例子:

代码语言:javascript
复制
e = 429

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

print(e)

PS:except Exception as e 可以捕获除了与程序退出(sys.exit())相关之外的所有异常。

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

出现的结果如下:

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

竟然报错了,那么这到底是为什么呢?

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

代码语言:javascript
复制
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 长的一毛一样?

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

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

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

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

代码语言:javascript
复制
>>> a = [1,2,3]
>>> b = a
>>> a = a + [4,5,6]

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

代码语言:javascript
复制
>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3]

估计很多人都会答对,表达式 a = a + [4,5,6] ,等号右边其实是形成了一个新的列表,最后让 a 引用了这个新的列表,而 b = a 引用的是之前的 a,所以 b 保持不变。

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

代码语言:javascript
复制
>>> a = [1,2,3]
>>> b = a
>>> a += [4,5,6]

上面的例子和文章开头的例子区别在从 + 变成了 +=,按照我们惯性思维去想,肯定以为这俩例子就是一个东西的两种不同写法而已,可实际上真的是这样吗?让我们来看一下此时的 a,b:

代码语言:javascript
复制
>>> 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 仍然指向已被修改的同一列表。

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

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

代码语言:javascript
复制
>>> 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 中的一个重要表现,那就是小括号还能表示“元组” 这一不可变数据类型

代码语言:javascript
复制
>>> type(())
<class 'tuple'>
>>> tur = (1, 2)
>>> type(tur)
<class 'tuple'>

但是如果小括号内只有一个元素的话,比如像下面这样,它就是小括号内的元素类型:

代码语言:javascript
复制
>>> tur = (1)
>>> type(tur)
<class 'int'>

天呐个天...

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

代码语言:javascript
复制
>>> tur = (1, )
>>> type(tur)
<class 'tuple'>

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

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

假如我们有一个列表,我想删除列表中的元素:

代码语言:javascript
复制
>>> lst = [1, 2, 3, 4, 5]
>>> for i in lst:
... lst.remove(i)

在刚开始学习 Python 之初,这是很多同学容易想到的做法,然而我们来看一下程序运行完以后的结果:

代码语言:javascript
复制
>>> lst
[2, 4]

看到这个结果,我……?

这是为啥子呢?是因为在 for 循环中,如果我们删除了 index = 0 (即 1)的值,原本 index = 1 及其之后的值会向前补位,所以当前 index = 1 的值为之前 index = 2 的值。

列表的删除操作我们经常要用,所以大家要打起十二分的精神来对它。

0x04 is not 不分家

is not 在 Python 中是一伙的,用的时候要靠在一起,分开以后就是两个东西,结果会不一样...

代码语言:javascript
复制
>>> [1, 2, 3] is not None
True
>>> [1, 2, 3] is (not None)
False

这就在于,is not 是个单独的二元运算符,当它两侧的变量指向同一个对象的时候,结果为 False,否则结果为 True,希望引起大家的注意...

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python空间 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档