Python黑魔法手册 2.0 文档第一章【25-30】
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 循环也是同样的。