Python黑魔法手册 2.0 文档第一章【25-30】

Python2年前 (2022)发布 safedragon
87 0

Python黑魔法手册

第一章:魔法冷知识【25-30】

1.25 迷一样的字符串

示例一

# Python2.7
>>> a = "Hello_Python"
>>> id(a)
32045616
>>> id("Hello" + "_" + "Python")
32045616
# Python3.7
>>> a = "Hello_Python"
>>> id(a)
38764272
>>> id("Hello" + "_" + "Python")
32045616

示例二

>>> a = "MING"
>>> b = "MING"
>>> a is b
True
# Python2.7
>>> a, b = "MING!", "MING!"
>>> a is b
True
# Python3.7
>>> a, b = "MING!", "MING!"
>>> a is b
False

示例三

# Python2.7
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
False
# Python3.7
>>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'
True
>>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'
True

1.26 x 与 +x 等价吗?

在大多数情况下,这个等式是成立的。

>>> n1 = 10086
>>> n2 = +n1
>>>
>>> n1 == n2
True

什么情况下,这个等式会不成立呢?

由于Counter的机制, + 用于两个 Counter 实例相加,而相加的结果如果元素的个数 <= 0,就会被丢弃。

>>> from collections import Counter
>>> ct = Counter('abcdbcaa')
>>> ct
Counter({'a': 3, 'b': 2, 'c': 2, 'd': 1})
>>> ct['c'] = 0
>>> ct['d'] = -2
>>>
>>> ct
Counter({'a': 3, 'b': 2, 'c': 0, 'd': -2})
>>>
>>> +ct
Counter({'a': 3, 'b': 2})

1.27 += 不等同于=+

对列表 进行 += 操作相当于 extend,而使用 =+ 操作是新增了一个列表。

因此会有如下两者的差异。

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

1.28 循环中的局部变量泄露

在Python 2中 x 的值在一个循环执行之后被改变了。

# Python2
>>> x = 1
>>> [x for x in range(5)]
[0, 1, 2, 3, 4]
>>> x
4

不过在Python3 中这个问题已经得到解决了。

# Python3
>>> x = 1
>>> [x for x in range(5)]
[0, 1, 2, 3, 4]
>>> x
1

1.29 局部/全局变量傻傻分不清

# demo.py
a = 1
def add():
 a += 1
add()

看似没有毛病,但实则已经犯了一个很基础的问题,运行结果如下:

$ python demo.py
Traceback (most recent call last):
 File "demo.py", line 6, in <module>
 add()
 File "demo.py", line 4, in add
 a += 1
UnboundLocalError: local variable 'a' referenced before assignment

回顾一下,什么是局部变量?在非全局下定义声明的变量都是局部变量。

当程序运行到 a += 1 时,Python 解释器就认为在函数内部要给 a 这个变量赋值,当然就把a 当做局部变量了,但是做为局部变量的 a 还没有被还没被定义。

因此报错是正常的。

理解了上面的例子,给你留个思考题。为什么下面的代码不会报错呢?

$ cat demo.py
a = 1
def output():
 print(a)
output()
$ python demo.py
1

1.30 break /continue 和 上下文管理器哪个优先级高?

众所周知,在循环体中(无论是 for 还是 while),continue会用来跳入下一个循环而 break 则用来跳出某个循环体。

同时我们又知道:在上下文管理器中,被包裹的程序主体代码结束会运行上下文管理器中的一段代码(通常是资源的释放)。

但如果把上下文管理器放在一个循环体中,而在这个上下文管理器中执行了 break ,是否会直接跳出循环呢?

换句话说,上下文管理器与 break/continue 这两个规则哪一个会优先级会更高一些?

这个问题其实不难,只要做一下试验都能轻易地得出答案,难就难在很多对这个答案都是半猜半疑,无法肯定的回答。

试验代码如下:

import time
import contextlib
@contextlib.contextmanager
def runtime(value):
 time.sleep(1)
 print("start: a = " + str(value))
 yield
 print("end: a = " + str(value))
a = 0
while True:
 a+=1
 with runtime(a):
 if a % 2 == 0:
 break

从输出的结果来看,当 a = 2 时执行了 break ,此时的并不会直接跳出循环,依然要运行上下文管理器里清理释放资源的代码(示例中,我使用 print 来替代)。

start: a = 1
end: a = 1
start: a = 2
end: a = 2

另外还有几个与此类似的问题,我这里也直接给出答案,不再细说了

1. continue 与 break 一样,如果先遇到上下文管理器会先进行资源的释放。

2.上面只举例了 while 循环体,而for 循环也是同样的。

© 版权声明

相关文章

暂无评论

暂无评论...