技巧:学会看源码(很重要,学习别人写代码的风格,解决问题的思路)
养成用思维导图,尤其是学习新东西 MindMaster
多上stack overflow,解决问题,还能看优秀代码,练英语
GitHub 搜索需要学习的内容
书签软件:登录 git 账号 pycharm 使用说明 kazoo库介绍 大神文章
软件使用:
服务器之间文件传输用 sftp,filezilla
连接服务器用 MobaXterm_Personal,putty
截图工具 Snipaste Navicat破解版
Python大纲图
并发与并行的区别;
并发:通过线程与任务之间的互相切换的方式实现,但同一时刻,只允许有一个线程或任务执行
并行:则是指多个进程完全同步同时的执行
并发通常适合I/O操作频繁的场景,并行适合CPU heavy的场景
总结多线程和协程之间的共同点和区别:
共同点:
都是并发操作,多线程同一时间点只能有一个线程在执行,协程同一时间点只能有一个任务在执行;
不同点:
多线程,是在I/O阻塞时通过切换线程来达到并发的效果,在什么情况下做线程切换是由操作系统来决定的,开发者不用操心,但会造成race condition;
协程,只有一个线程,在I/O阻塞时通过在线程内切换任务来达到并发的效果,在什么情况下做任务切换是开发者决定的,不会有race condition的情况;
多线程的线程切换比协程的任务切换开销更大;
对于开发者而言,多线程并发的代码比协程并发的更容易书写。
一般情况下协程并发的处理效率比多线程并发更高。
资源竞争(race condition):多线程会出现,协程不会出现。
多个线程在执行的过程中会出现资源竞争(race condition)的情况;有可能会导致同一个资源/数据被多个线程争夺的情况,这样会导致最后的结果可能会和期望的结果不一致的各种特殊情况吧。
Python GIL 全局解释器锁:
针对Cpython,为了保护线程安全。
但是为什么在多线程编写时需要再加入锁,因为:GIL是每隔一段时间会自动释放,Python3以后是15毫秒或者遇到I/O操作,线程还是会去争抢资源
根本原因:GIL 是为了方便CPython解释器层面的编写者,而不是Python应用层面的程序员。
绕过GIL俩种办法:1、不用Cpython 用JPython(Java实现的)2、把关键性代码,放到别的语言中(一般是C++)
为什么别的语言没有(Cpython为什么这么设计):
1、设计者为了规避类似内存管理这样复杂的竞争风险问题
2、Cpython使用了大量的C语言库,但大部分的C语言库都不是原生线程安全的。这是一个取舍问题,安全了必然会舍弃性能和增加复杂度
“python下想要充分利用多核CPU,就用多进程”,原因是什么呢?原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。
所以我们能够得出结论:多核下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率。
组合与继承:
组合与继承都是有效地利用已有类的资源的重要方式
组合:
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
继承:
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师
Python垃圾回收机制
针对循环引用问题:
1、标记清除算法。用图论理解不可达理论,对于一个有向图,如果从一个节点出发遍历,并标记其经过的所有节点;那么在遍历后,所有没有被标记的节点我们称为不可达节点,自然这些我们就要进行垃圾回收。
当然,每次遍历图对Python说很浪费性能,所有实际中 mark_sweep使用双向链表维护了一个数据结构,并且只考虑容器类的对象(只有容器才会产生循环引用)
2、分代收集:分代收集基于的思想是,新生的对象更有可能被垃圾回收,而存活更更久的对象也有更大几率继续存活。因此这种方法节约不少计算量。
参考文章:
为什么多线程里有一个挂掉(内存溢出),都会出问题,因为对于多线程来说,由于同一个进程空间中存在多个栈,任何一个空白区域被填满都会导致stack overflow的问题。
多线程
我们先来看一下什么是多线程。在Linux从程序到进程中,我们看到了一个程序在内存中的表示。这个程序的整个运行过程中,只有一个控制权的存在。当函数被调用的时候,该函数获得控制权,成为激活(active)函数,然后运行该函数中的指令。与此同时,其它的函数处于离场状态,并不运行。如下图所示:
Linux从程序到进程
我们看到,各个方块之间由箭头连接。各个函数就像是连在一根线上一样,计算机像一条流水线一样执行各个函数中定义的操作。这样的一个程序叫做单线程程序。
多线程就是允许一个进程内存在多个控制权,以便让多个函数同时处于激活状态,从而让多个函数的操作同时运行。即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果。如下图所示,就是一个多线程的流程:
main()到func3()再到main()构成一个线程,此外func1()和func2()构成另外两个线程。操作系统一般都有一些系统调用来让你将一个函数运行成为一个新的线程。
回忆我们在Linux从程序到进程中提到的栈的功能和用途。一个栈,只有最下方的帧可被读写。相应的,也只有该帧对应的那个函数被激活,处于工作状态。为了实现多线程,我们必须绕开栈的限制。为此,创建一个新的线程时,我们为这个线程建一个新的栈。每个栈对应一个线程。当某个栈执行到全部弹出时,对应线程完成任务,并收工。所以,多线程的进程在内存中有多个栈。多个栈之间以一定的空白区域隔开,以备栈的增长。每个线程可调用自己栈最下方的帧中的参数和变量,并与其它线程共享内存中的Text,heap和global data区域。对应上面的例子,我们的进程空间中需要有3个栈。
(要注意的是,对于多线程来说,由于同一个进程空间中存在多个栈,任何一个空白区域被填满都会导致stack overflow的问题。)
python中的可hash和不可变性
1.如果一个对象在自己的生命周期中有一哈希值(hash value)是不可改变的,那么它就是可哈希的(hashable)的,因为这些数据结构内置了哈希值,每个可哈希的对象都内置了__hash__方法,所以可哈希的对象可以通过哈希值进行对比,也可以作为字典的键值和作为set函数的参数。所有python中所有不可改变的的对象(imutable objects)都是可哈希的,比如字符串,元组,也就是说可改变的容器如字典,列表不可哈希(unhashable)。我们用户所定义的类的实例对象默认是可哈希的(hashable),它们都是唯一的,而hash值也就是它们的id()。
2. 哈希
它是一个将大体量数据转化为很小数据的过程,甚至可以仅仅是一个数字,以便我们可以用在固定的时间复杂度下查询它,所以,哈希对高效的算法和数据结构很重要。
3. 不可改变性
它指一些对象在被创建之后不会因为某些方式改变,特别是针对任何可以改变哈希对象的哈希值的方式
4. 联系:
因为哈希键一定是不可改变的,所以它们对应的哈希值也不改变。如果允许它们改变,,那么它们在数据结构如哈希表中的存储位置也会改变,因此会与哈希的概念违背,效率会大打折扣
Python闭包
一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。
闭包其实就是保存上下文环境。所谓的闭包是一个包含有环境变量取值的函数对象。环境变量取值被保存在函数对象的__closure__属性中。