列表self.append无限嵌套的原理
如下述代码所示:
1 | x = [1] |
上述操作的示意如下:
虽然x
是无限嵌套的列表,但是x.append(x)
操作并不会遍历其中的每一个元素,只是扩充了原列表的第二个元素,并将其指向x
,因而不会出现栈溢出的问题。同时,len(x)
的返回为2,因为x
的top level只有两个元素。
装饰器的宏观理解
装饰器的作用与意义在于其可以通过自定义的函数或类,在不改变原函数的基础上,改变原函数的一些功能。装饰器将额外增加的功能封装在自己的装饰器函数或类中;如果想要调用该装饰器,只需要在原函数的顶部,加上@decorator
即可。
例如,在一些社交网络的后台,有无数的操作在调用之前,需要执行一些检查操作。使用常规编程的方式如下:
1 | # 发表评论 |
反复调用authenticate
显得非常冗余。更好的解决方法是将authenticate
单独分离出来,写成装饰器,如下所示:
1 | # 发表评论 |
使用装饰器有以下好处:
- 代码更加简洁;
- 逻辑更加清晰;
- 程序的层次化、分离化更加明显。
GIL和多线程的关系
GIL只支持单线程,而Python支持多线程,这两者之间是什么关系?
GIL的存在和Python支持多线程不矛盾,GIL指的是同一时刻,程序只能有一个线程运行;而Python中的多线程是多个线程交替执行,是“伪并行”,具体到某一个时刻,只有1个线程在运行。
例如,在爬虫时,线程1在爬取第一个网站时发生I/O阻塞,处于等待状态,此时,GIL就会被释放,线程2开始执行,以此类推。等到线程1的I/O操作完成时,主程序便会切换回线程1,完成剩余工作。
多进程和多线程的适用场景
多进程适用于CPU密集型任务,如大型矩阵运算等,对于CPU密集任务,多线程无效,因为Python的多线程一个时刻只会有一个线程运行。
而多线程或Asyncio适用于I/O密集型任务,但如果I/O操作非常多、非常heavy,需要建立的连接也比较多时,一般会选择Asyncio。因为Asyncio的任务切换更为轻量化,且能启动的任务数远比多线程能启动的线程数多。