python高级

master
kenwoodjw 2019-03-03 01:08:57 +08:00
parent 5be81c15a6
commit 1d1bd177e8
1 changed files with 70 additions and 70 deletions

140
README.md
View File

@ -564,8 +564,8 @@ New 是真正创建实例对象的方法所以重写基类的new 方法,以
``` ```
## 4.3 单例模式的应用场景有那些? ## 4.3 单例模式的应用场景有那些?
单例模式应用的场景一般发现在以下条件下: 单例模式应用的场景一般发现在以下条件下:
资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。 资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。
控制资源的情况下方便资源之间的互相通信。如线程池等1,网站的计数器 2,应用配置 3.多线程池 4数据库配置 数据库连接池 5.应用程序的日志应用... 控制资源的情况下方便资源之间的互相通信。如线程池等1,网站的计数器 2,应用配置 3.多线程池 4数据库配置 数据库连接池 5.应用程序的日志应用...
## 4.5 对装饰器的理解,并写出一个计时器记录方法执行性能的装饰器? ## 4.5 对装饰器的理解,并写出一个计时器记录方法执行性能的装饰器?
装饰器本质上是一个python函数它可以让其他函数在不需要做任何代码变动的前提下增加额外功能装饰器的返回值也是一个函数对象。 装饰器本质上是一个python函数它可以让其他函数在不需要做任何代码变动的前提下增加额外功能装饰器的返回值也是一个函数对象。
@ -590,8 +590,8 @@ New 是真正创建实例对象的方法所以重写基类的new 方法,以
装饰器本质上是一个python函数它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装饰器的返回值也是一个函数的对象它经常用于有切面需求的场景。比如插入日志性能测试事务处理缓存。权限的校验等场景有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。 装饰器本质上是一个python函数它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装饰器的返回值也是一个函数的对象它经常用于有切面需求的场景。比如插入日志性能测试事务处理缓存。权限的校验等场景有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。
## 4.8 生成器,迭代器的区别? ## 4.8 生成器,迭代器的区别?
迭代器是一个更抽象的概念任何对象如果它的类有next方法和iter方法返回自己本身对于string,list,dict,tuple等这类容器对象使用for循环遍历是很方便的在后台for语句对容器对象调用iter()函数iter()是python的内置函数iter()会返回一个定义了next()方法的迭代器对象它在容器中逐个访问容器内元素next()也是python的内置函数在没有后续元素时next()会抛出一个StopIteration异常。 迭代器是一个更抽象的概念任何对象如果它的类有next方法和iter方法返回自己本身对于string,list,dict,tuple等这类容器对象使用for循环遍历是很方便的在后台for语句对容器对象调用iter()函数iter()是python的内置函数iter()会返回一个定义了next()方法的迭代器对象它在容器中逐个访问容器内元素next()也是python的内置函数在没有后续元素时next()会抛出一个StopIteration异常。
生成器Generator是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值) 生成器Generator是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
区别: 生成器能做到迭代器能做的所有事而且因为自动创建iter()和next()方法生成器显得特别简洁而且生成器也是高效的使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法当发生器终结时还会自动抛出StopIteration异常。 区别: 生成器能做到迭代器能做的所有事而且因为自动创建iter()和next()方法生成器显得特别简洁而且生成器也是高效的使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法当发生器终结时还会自动抛出StopIteration异常。
## 4.9 X是什么类型? ## 4.9 X是什么类型?
X= (fo ri in ramg(10)) X= (fo ri in ramg(10))
X是 generator类型 X是 generator类型
@ -604,18 +604,18 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
## 7系统编程 ## 7系统编程
## 7.1 进程总结 ## 7.1 进程总结
进程程序运行在操作系统上的一个实例就称之为进程。进程需要相应的系统资源内存、时间片、pid。 进程程序运行在操作系统上的一个实例就称之为进程。进程需要相应的系统资源内存、时间片、pid。
创建进程: 创建进程:
首先要导入multiprocessing中的Process 首先要导入multiprocessing中的Process
创建一个Process对象; 创建一个Process对象;
创建Process对象时可以传递参数; 创建Process对象时可以传递参数;
``` ```
p = Process(target=XXX,args=(tuple,),kwargs={key:value}) p = Process(target=XXX,args=(tuple,),kwargs={key:value})
target = XXX 指定的任务函数,不用加(), target = XXX 指定的任务函数,不用加(),
args=(tuple,)kwargs={key:value}给任务函数传递的参数 args=(tuple,)kwargs={key:value}给任务函数传递的参数
``` ```
使用start()启动进程 使用start()启动进程
结束进程 结束进程
给子进程指定函数传递参数Demo 给子进程指定函数传递参数Demo
``` ```
import os import os
from mulitprocessing import Process from mulitprocessing import Process
@ -637,19 +637,19 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
p.join() p.join()
``` ```
注意:进程间不共享全局变量 注意:进程间不共享全局变量
进程之间的通信-Queue 进程之间的通信-Queue
在初始化Queue()对象时例如q=Queue(),若在括号中没有指定最大可接受的消息数量,获数量为负值时,那么就代表可接受的消息数量没有上限一直到内存尽头) 在初始化Queue()对象时例如q=Queue(),若在括号中没有指定最大可接受的消息数量,获数量为负值时,那么就代表可接受的消息数量没有上限一直到内存尽头)
Queue.qsize():返回当前队列包含的消息数量 Queue.qsize():返回当前队列包含的消息数量
Queue.empty():如果队列为空返回True反之False Queue.empty():如果队列为空返回True反之False
Queue.full():如果队列满了返回True,反之False Queue.full():如果队列满了返回True,反之False
Queue.get([block[,timeout]]):获取队列中的一条消息然后将其从队列中移除block默认值为True。 Queue.get([block[,timeout]]):获取队列中的一条消息然后将其从队列中移除block默认值为True。
如果block使用默认值且没有设置timeout单位秒),消息队列如果为空此时程序将被阻塞停在读中状态直到消息队列读到消息为止如果设置了timeout则会等待timeout秒若还没读取到任何消息则抛出“Queue.Empty"异常: 如果block使用默认值且没有设置timeout单位秒),消息队列如果为空此时程序将被阻塞停在读中状态直到消息队列读到消息为止如果设置了timeout则会等待timeout秒若还没读取到任何消息则抛出“Queue.Empty"异常:
Queue.get_nowait()相当于Queue.get(False) Queue.get_nowait()相当于Queue.get(False)
Queue.put(item,[block[,timeout]]):将item消息写入队列block默认值为True; Queue.put(item,[block[,timeout]]):将item消息写入队列block默认值为True;
如果block使用默认值且没有设置timeout单位秒消息队列如果已经没有空间可写入此时程序将被阻塞停在写入状态直到从消息队列腾出空间为止如果设置了timeout则会等待timeout秒若还没空间则抛出”Queue.Full"异常 如果block使用默认值且没有设置timeout单位秒消息队列如果已经没有空间可写入此时程序将被阻塞停在写入状态直到从消息队列腾出空间为止如果设置了timeout则会等待timeout秒若还没空间则抛出”Queue.Full"异常
如果block值为False消息队列如果没有空间可写入则会立刻抛出"Queue.Full"异常; 如果block值为False消息队列如果没有空间可写入则会立刻抛出"Queue.Full"异常;
Queue.put_nowait(item):相当Queue.put(item,False) Queue.put_nowait(item):相当Queue.put(item,False)
进程间通信Demo: 进程间通信Demo:
``` ```
from multiprocessing import Process.Queue from multiprocessing import Process.Queue
import os,time,random import os,time,random
@ -707,8 +707,8 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
print("----end----") print("----end----")
``` ```
进程池中使用Queue 进程池中使用Queue
如果要使用Pool创建进程就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到如下的错误信息: 如果要使用Pool创建进程就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到如下的错误信息:
RuntimeError Queue objects should only be shared between processs through inheritance RuntimeError Queue objects should only be shared between processs through inheritance
``` ```
from multiprocessing import Manager,Pool from multiprocessing import Manager,Pool
import os,time,random import os,time,random
@ -734,17 +734,17 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
``` ```
## 7.2 谈谈你对多进程,多线程,以及协程的理解,项目是否用? ## 7.2 谈谈你对多进程,多线程,以及协程的理解,项目是否用?
这个问题被问的概念相当之大, 这个问题被问的概念相当之大,
进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。 进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。
线程: cpu调度执行的最小单位也叫执行路径不能独立存在依赖进程存在一个进程至少有一个线程叫主线程而多个线程共享内存数据共享共享全局变量),从而极大地提高了程序的运行效率。 线程: cpu调度执行的最小单位也叫执行路径不能独立存在依赖进程存在一个进程至少有一个线程叫主线程而多个线程共享内存数据共享共享全局变量),从而极大地提高了程序的运行效率。
协程: 是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操中栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。 协程: 是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操中栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
## 7.3 Python异常使用场景有那些 ## 7.3 Python异常使用场景有那些
异步的使用场景: 异步的使用场景:
1、 不涉及共享资源,获对共享资源只读,即非互斥操作 1、 不涉及共享资源,获对共享资源只读,即非互斥操作
2、 没有时序上的严格关系 2、 没有时序上的严格关系
3、 不需要原子操作,或可以通过其他方式控制原子性 3、 不需要原子操作,或可以通过其他方式控制原子性
4、 常用于IO操作等耗时操作因为比较影响客户体验和使用性能 4、 常用于IO操作等耗时操作因为比较影响客户体验和使用性能
5、 不影响主线程逻辑 5、 不影响主线程逻辑
## 7.4 多线程共同操作同一个数据互斥锁同步? ## 7.4 多线程共同操作同一个数据互斥锁同步?
``` ```
@ -771,14 +771,14 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
``` ```
## 7.5 什么是多线程竞争? ## 7.5 什么是多线程竞争?
线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:数据几乎同步会被多个线程占用,造成数据混乱,即所谓的线程不安全 线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:数据几乎同步会被多个线程占用,造成数据混乱,即所谓的线程不安全
那么怎么解决多线程竞争问题?---锁 那么怎么解决多线程竞争问题?---锁
锁的好处: 确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。 锁的好处: 确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。
锁的坏处: 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了 锁的坏处: 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
锁的致命问题: 死锁 锁的致命问题: 死锁
## 7.6 请介绍一下Python的线程同步 ## 7.6 请介绍一下Python的线程同步
一、 setDaemon(False) 一、 setDaemon(False)
当一个进程启动之后会默认产生一个主线程因为线程是程序执行的最小单位当设置多线程时主线程会创建多个子线程在Python中默认情况下就是setDaemon(False),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束。 当一个进程启动之后会默认产生一个主线程因为线程是程序执行的最小单位当设置多线程时主线程会创建多个子线程在Python中默认情况下就是setDaemon(False),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束。
例子 例子
``` ```
import threading import threading
import time import time
@ -799,8 +799,8 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
---子线程结束--- ---子线程结束---
``` ```
二、 setDaemonTrue) 二、 setDaemonTrue)
当我们使用setDaemon(True)时,这是子线程为守护线程,主线程一旦执行结束,则全部子线程被强制终止 当我们使用setDaemon(True)时,这是子线程为守护线程,主线程一旦执行结束,则全部子线程被强制终止
例子 例子
``` ```
import threading import threading
import time import time
@ -819,10 +819,10 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
---主线程结束--- #只有主线程结束,子线程来不及执行就被强制结束 ---主线程结束--- #只有主线程结束,子线程来不及执行就被强制结束
``` ```
三、 join线程同步) 三、 join线程同步)
join 所完成的工作就是线程同步,即主线程任务结束以后,进入堵塞状态,一直等待所有的子线程结束以后,主线程再终止。 join 所完成的工作就是线程同步,即主线程任务结束以后,进入堵塞状态,一直等待所有的子线程结束以后,主线程再终止。
当设置守护线程时含义是主线程对于子线程等待timeout的时间将会杀死该子线程最后退出程序所以说如果有10个子线程全部的等待时间就是每个timeout的累加和简单的来说就是给每个子线程一个timeou的时间让他去执行时间一到不管任务有没有完成直接杀死。 当设置守护线程时含义是主线程对于子线程等待timeout的时间将会杀死该子线程最后退出程序所以说如果有10个子线程全部的等待时间就是每个timeout的累加和简单的来说就是给每个子线程一个timeou的时间让他去执行时间一到不管任务有没有完成直接杀死。
没有设置守护线程时主线程将会等待timeout的累加和这样的一段时间时间一到主线程结束但是并没有杀死子线程子线程依然可以继续执行直到子线程全部结束程序退出。 没有设置守护线程时主线程将会等待timeout的累加和这样的一段时间时间一到主线程结束但是并没有杀死子线程子线程依然可以继续执行直到子线程全部结束程序退出。
例子 例子
``` ```
import threading import threading
import time import time
@ -848,48 +848,48 @@ yield就是保存当前程序执行状态。你用for循环的时候每次取
## 7.8 什么是死锁? ## 7.8 什么是死锁?
若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。 若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。
GIL锁 全局解释器锁只在cython里才有 GIL锁 全局解释器锁只在cython里才有
作用: 限制多线程同时执行保证同一时间只有一个线程执行所以cython里的多线程其实是伪多线程 作用: 限制多线程同时执行保证同一时间只有一个线程执行所以cython里的多线程其实是伪多线程
所以python里常常使用协程技术来代替多线程协程是一种更轻量级的线程。 所以python里常常使用协程技术来代替多线程协程是一种更轻量级的线程。
进程和线程的切换时由系统决定而协程由我们程序员自己决定而模块gevent下切换是遇到了耗时操作时才会切换 进程和线程的切换时由系统决定而协程由我们程序员自己决定而模块gevent下切换是遇到了耗时操作时才会切换
三者的关系:进程里有线程,线程里有协程。 三者的关系:进程里有线程,线程里有协程。
## 7.9 多线程交互访问数据,如果访问到了就不访问了? ## 7.9 多线程交互访问数据,如果访问到了就不访问了?
怎么避免重读? 怎么避免重读?
创建一个已访问数据列表,用于存储已经访问过的数据,并加上互斥锁,在多线程访问数据的时候先查看数据是否在已访问的列表中,若已存在就直接跳过。 创建一个已访问数据列表,用于存储已经访问过的数据,并加上互斥锁,在多线程访问数据的时候先查看数据是否在已访问的列表中,若已存在就直接跳过。
## 7.10 什么是线程安全,什么是互斥锁? ## 7.10 什么是线程安全,什么是互斥锁?
每个对象都对应于一个可称为’互斥锁‘的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。 每个对象都对应于一个可称为’互斥锁‘的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
同一进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。 同一进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。
## 7.11说说下面几个概念:同步,异步,阻塞,非阻塞? ## 7.11说说下面几个概念:同步,异步,阻塞,非阻塞?
同步: 多个任务之间有先后顺序执行,一个执行完下个才能执行。 同步: 多个任务之间有先后顺序执行,一个执行完下个才能执行。
异步: 多个任务之间没有先后顺序,可以同时执行,有时候一个任务可能要在必要的时候获取另一个同时执行的任务的结果,这个就叫回调! 异步: 多个任务之间没有先后顺序,可以同时执行,有时候一个任务可能要在必要的时候获取另一个同时执行的任务的结果,这个就叫回调!
阻塞: 如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。 阻塞: 如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。
非阻塞: 如果不会卡住,可以继续执行,就是说非阻塞的。 非阻塞: 如果不会卡住,可以继续执行,就是说非阻塞的。
同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。 同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。
## 7.12 什么是僵尸进程和孤儿进程?怎么避免僵尸进程? ## 7.12 什么是僵尸进程和孤儿进程?怎么避免僵尸进程?
孤儿进程: 父进程退出子进程还在运行的这些子进程都是孤儿进程孤儿进程将被init 进程进程号为1所收养并由init 进程对他们完成状态收集工作。 孤儿进程: 父进程退出子进程还在运行的这些子进程都是孤儿进程孤儿进程将被init 进程进程号为1所收养并由init 进程对他们完成状态收集工作。
僵尸进程: 进程使用fork 创建子进程如果子进程退出而父进程并没有调用wait 获waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。 僵尸进程: 进程使用fork 创建子进程如果子进程退出而父进程并没有调用wait 获waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。
避免僵尸进程的方法: 避免僵尸进程的方法:
1.fork 两次用孙子进程去完成子进程的任务 1.fork 两次用孙子进程去完成子进程的任务
2.用wait()函数使父进程阻塞 2.用wait()函数使父进程阻塞
3.使用信号量在signal handler 中调用waitpid,这样父进程不用阻塞 3.使用信号量在signal handler 中调用waitpid,这样父进程不用阻塞
## 7.13 python中进程与线程的使用场景 ## 7.13 python中进程与线程的使用场景
多进程适合在CPU密集操作cpu操作指令比较多如位多的的浮点运算 多进程适合在CPU密集操作cpu操作指令比较多如位多的的浮点运算
多线程适合在IO密性型操作读写数据操作比多的的比如爬虫 多线程适合在IO密性型操作读写数据操作比多的的比如爬虫
## 7.14 线程是并发还是并行,进程是并发还是并行? ## 7.14 线程是并发还是并行,进程是并发还是并行?
线程是并发,进程是并行; 线程是并发,进程是并行;
进程之间互相独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。 进程之间互相独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。
## 7.15 并行(parallel)和并发concurrency)? ## 7.15 并行(parallel)和并发concurrency)?
并行: 同一时刻多个任务同时在运行 并行: 同一时刻多个任务同时在运行
并发: 在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。 并发: 在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。
实现并行的库有: multiprocessing 实现并行的库有: multiprocessing
实现并发的库有: threading 实现并发的库有: threading
程序需要执行较多的读写、请求和回复任务的需要大量的IO操作IO密集型操作使用并发更好。 程序需要执行较多的读写、请求和回复任务的需要大量的IO操作IO密集型操作使用并发更好。
CPU运算量大的程序使用并行会更好 CPU运算量大的程序使用并行会更好
## 7.16 IO密集型和CPU密集型区别 ## 7.16 IO密集型和CPU密集型区别
IO密集型 系统运行大部分的状况是CPU在等 I/O硬盘/内存)的读/写 IO密集型 系统运行大部分的状况是CPU在等 I/O硬盘/内存)的读/写
CPU密集型 大部分时间用来做计算逻辑判断等CPU动作的程序称之CPU密集型。 CPU密集型 大部分时间用来做计算逻辑判断等CPU动作的程序称之CPU密集型。