Python基础
Python2 与 3的区别
print语句被python3废弃,改为print函数
Python3中字符串是Unicode (utf-8)编码、python2中是ASCII编码
异常处理 Python2中try:...except Exception, e:...,在Python3中改为了try:...except Exception as e:...
Python3中不再使用xrange方法,只有range方法。
range在Python2中返回列表,而在Python3中返回range可迭代对象。
python 2 中通过input输入的类型是int,只有通过raw_input()输入的类型才是str。
进程 线程 协程
进程
进程,是执行中的计算机程序。也就是说,每个代码在执行的时候,首先本身即是一个进程。
进程是是资源分配的最小单位 运行在内存中
每个进程最少有一个线程,进程不干活让线程干
进程可以通过派生新的进程来执行其它任务,不过每个进程还是都拥有自己的内存和数据栈等。
一个进程具有:就绪,运行,中断,僵死,结束等状态(不同操作系统不一样)。
进程支持并发和并行
进程通信
进程之间数据不共享 所以需要进程通信
通信的三种方式
1 进程队列queue
2 管道pipe
3 共享数据manage
进程说明
多个进程可以在不同的 CPU 上运行,互不干扰
同一个CPU上,可以运行多个进程,由操作系统来自动分配时间片
进程池
开多进程是为了并发,通常有几个cpu核心就开几个进程,但是进程开多了会影响效率,主要体现在切换的开销,所以引入进程池限制进程的数量。
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
线程
线程,是在进程中执行的代码。线程之间 数据共享
一个进程下可以运行多个线程,这些线程之间共享主进程内申请的操作系统资源。
线程是系统调度的最小单位
常用方法
join()方法
实现所有线程都执行结束后再执行主线程
如果一个线程或者在函数执行的过程中调用另一个线程,并且希望待其完成操作后才能执行, 那么在 调用线程的时就可以使用被调线程的join方法join([timeout]) timeout:可选参数,线程运行的最长时间
isAlive()方法
查看线程是否还在运行
getName()方法
获得线程名
setDaemon()方法
主线程退出时,需要子线程随主线程退出,则设置子线程的setDaemon() 也叫守护线程
线程锁
线程锁也叫用户锁 也叫互斥锁、当前线程还未操作完成前其他所有线程都无法对其操作,即使已经释放了GIL锁
线程特性
线程,必须在一个存在的进程中启动运行
线程使用进程获得的系统资源,不会像进程那样需要申请CPU等资源
线程无法给予公平执行时间,它可以被其他线程抢占,而进程按照操作系统的设定分配执行时间
线程池
系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。在这种情形下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。
线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。
进程和线程的区别
一个进程中的各个线程与主进程共享相同的资源,与进程间互相独立相比,线程之间信息共享和通信更加容易
线程一般以并发执行,正是由于这种并发和数据共享机制,使多任务间的协作成为可能。
进程一般以并行执行,这种并行能使得程序能同时在多个CPU上运行;
协程
协程能在单线程的条件下支持并发,能遇IO自动切换、将网络数据或磁盘数据写入到内存就是一个IO操作
协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用,且看似像多线程 实际就是单线程
协程由于由程序主动控制切换,没有线程切换的开销,所以执行效率极高。对于IO密集型任务非常适用,如果是cpu密集型,推荐多进程+协程的方式。
协程优点
1 无需线程上下文切换的开销
2 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突
3 单线程内就可以实现并发的效果,最大限度地利用cpu
协程缺点
无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上
线程阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
协程为何能处理并发
Greenlet
遇IO操作 手动切换 、是c语言开发的
Gevent
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程
其实Gevent模块仅仅是对greenlet的再封装,将I/O间的手动切换变成自动切换
Gevent原理是只要遇到I/O操作就会自动切换到下一个协程
猴子补丁
用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();
作用是把标准库中的thread/socket等给替换掉.无需修改任何代码,把它变成了非阻塞
说明
协程间是协同调度的,这使得并发量数万以上的时候,协程的性能是远远高于线程。注意这里也是“并发”,不是“并行”。 协程还有gevent和greenlet 2个第三方库、greenlet是c语言开发的、是遇IO手动切换、 gevent内封装了greenlet 遇IO自动切换
greenlet框架封装了yield语句、挂起函数,直到稍后使用next()或send()操作进行恢复为止
GIL全局解释器锁
GIL 保证同一时间内只有一个线程在执行、GIL并不是Python的特性
GIL只存在于使用C语言编写的解释器CPython中
有了GiL锁为何需要线程锁
因为CPU给当前线程分配了时间、时间结束后线程任务没完成所以需要线程锁
GiL有什么影响
GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。
排他锁
若事务T对数据对象A加上X锁,则只允许T读取和修改A
其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。
避免GiL带来的影响
方法一
用进程+协程 代替 多线程的方式 在多进程中,由于每个进程都是独立的存在,所以每个进程内的线程都拥有独立的GIL锁,互不影响。但是,由于进程之间是独立的存在,所以进程间通信就需要通过队列的方式来实现。
方法二
更换解释器
迭代器
迭代器的定义
迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。
凡是可作用于`next()`函数的对象都是迭代器(`Iterator)`类型,它们表示一个惰性计算的序列;
迭代器的2方法
next可以返回容器的下一个元素
a = iter([1,2,]) #生成一个迭代器
print(a.__next__())
print(a.__next__())
print(a.__next__()) #在这一步会引发 “StopIteration” 的异常
__ iter __返回迭代器本身
可迭代对象
凡是可作用于for
循环的对象都是可迭代的(Iterable)类型;
小结
一个实现了__iter__方法的对象是可迭代的,一个实现next方法的对象是迭代器
生成器
工作原理
生成器是这样一个函数,它记住上一次返回时在函数体中的位置。
对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
生成器的定义
生成器,即生成一个容器。
在Python中,**一边循环,一边计算的机制,称为生成器。**
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置iter()方法或__iter__()的内置函数),
所以,生成器就是一个可迭代对象。
生成器的作用
1. 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的。
2. 如: 创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
3. 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
4. 这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
5. 创建一个生成器
print( [i*2 for i in range(10)] ) #列表生成式: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
print( (i*2 for i in range(10)) ) #<generator object <genexpr> at0x005A3690>
#获取生成器内元素
g = (i*2 for i in range(10))
print( g.__next__() ) # 0
print( g.__next__() ) # 2
yield运行机制
在Python中,yield就是一个生成器。 如果在函数内使用了yield关键字时,这个函数就是一个生成器
当你问生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器把yield 的参数给你,之后生成器就不会往下继续运行。
当你问他要下一个数时,他会从上次的状态开始运行,直至出现yield语句,把参数给你,之后停下。如此反复
每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出。
手写斐波那契
def fbnq(n):
a,b=0,1
while n>a:
yield a
a,b=b,a+b
if __name__ == '__main__':
f=fbnq(9)
print(next(f)) #打印结果 0
print(next(f)) #打印结果 1
装饰器
装饰器作用
装饰器是在不修改源代码的情况下为其添加新的功能、 装饰器是基于闭包实现的
装饰器原则:
不能修改被装饰函数的源代码
不能修改被装饰函数的调用方式
手写三级装饰器
import time
user,passwd = 'aaa','123'
def auth(auth_type):
print("auth func:",auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
print("\033[32;1mUser has passed authentication\033[0m")
res = func(*args, **kwargs) # from home
print("---after authenticaion ")
return res
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print("搞毛线ldap,不会。。。。")
return wrapper
return outer_wrapper
def index():
print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
print("welcome to home page")
return "from home"
@auth(auth_type="ldap")
def bbs():
print("welcome to bbs page")
index()
print(home()) #wrapper()
bbs()
闭包
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包
但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
闭包特点
1. 必须有一个内嵌函数
2. 内嵌函数必须引用外部函数中的变量
3. 外部函数的返回值必须是内嵌函数
四大高阶函数
MAP
map(func, iter)函数:Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回
第一个参数接收一个函数名,第二个参数接收一个可迭代对象
功能:将传入的函数依次作用于序列中的每一个元素,并把结果作为一个新的迭代器返回.
注意:当传入map的序列只有一个的时候,传入的函数,有且只有一个参数.当map的序列有多个的情况下,得到的迭代长度与序列中最短的那个列表长度相同
def a(b):
return b*b #这里可以使用 算术运算符 任意一种
A=map(a,[1,2,3,4,5]) #map里2个参数 一个是函数名 一个是可迭代对象
print(list(A)) #把变量A 强转为列表 输出结果[1, 4, 9, 16, 25]
print(type(A)) #输出结果 <map object at 0x00000297DFE25080>
Reduce
reduce(func, iter)函数 :会对参数序列中元素进行累积
参数一:要传入的函数名
参数二:序列
功能:一个函数作用序列上,而且此函数必须接受两个参数, reduce把这次作用的结果,继续与序列中的下一个元素进行累计运算.
#reduc 函数 使用reduce必须先导入模块
from functools import reduce
def e (f,g):
return f+g
E=reduce(e