Python核心技术与实战-课程答疑总结(持续更新)

列表self.append无限嵌套的原理

如下述代码所示:

1
2
3
4
x = [1]
x.append(x)
x
[1, [...]]

上述操作的示意如下:

001a607f3f29f68975be3e706711325f

虽然x是无限嵌套的列表,但是x.append(x)操作并不会遍历其中的每一个元素,只是扩充了原列表的第二个元素,并将其指向x,因而不会出现栈溢出的问题。同时,len(x)的返回为2,因为x的top level只有两个元素。

装饰器的宏观理解

装饰器的作用与意义在于其可以通过自定义的函数或类,在不改变原函数的基础上,改变原函数的一些功能。装饰器将额外增加的功能封装在自己的装饰器函数或类中;如果想要调用该装饰器,只需要在原函数的顶部,加上@decorator即可。

例如,在一些社交网络的后台,有无数的操作在调用之前,需要执行一些检查操作。使用常规编程的方式如下:

1
2
3
4
5
6
7
8
9
10
11
# 发表评论
def post_comment(request, ...):
if not authenticate(request):
raise Exception('U must log in first')
...

# 发表状态
def post_moment(request, ...):
if not authenticate(request):
raise Exception('U must log in first')
...

反复调用authenticate显得非常冗余。更好的解决方法是将authenticate单独分离出来,写成装饰器,如下所示:

1
2
3
4
5
6
7
# 发表评论
@authenticate
def post_comment(request, ...):

# 发表状态
@authenticate
def post_moment(request, ...):

使用装饰器有以下好处:

  • 代码更加简洁;
  • 逻辑更加清晰;
  • 程序的层次化、分离化更加明显。

GIL和多线程的关系

GIL只支持单线程,而Python支持多线程,这两者之间是什么关系?

GIL的存在和Python支持多线程不矛盾,GIL指的是同一时刻,程序只能有一个线程运行;而Python中的多线程是多个线程交替执行,是“伪并行”,具体到某一个时刻,只有1个线程在运行。

img

例如,在爬虫时,线程1在爬取第一个网站时发生I/O阻塞,处于等待状态,此时,GIL就会被释放,线程2开始执行,以此类推。等到线程1的I/O操作完成时,主程序便会切换回线程1,完成剩余工作。

多进程和多线程的适用场景

多进程适用于CPU密集型任务,如大型矩阵运算等,对于CPU密集任务,多线程无效,因为Python的多线程一个时刻只会有一个线程运行。

而多线程或Asyncio适用于I/O密集型任务,但如果I/O操作非常多、非常heavy,需要建立的连接也比较多时,一般会选择Asyncio。因为Asyncio的任务切换更为轻量化,且能启动的任务数远比多线程能启动的线程数多。

参考