python基础(二)
一、继承、封装、多态
继承:
- python2:深度优先继承
- python3:广度优先继承
- python中的继承链:继承链中保存了继承顺序,同时指明函数的执行的找到顺序
- lion.mro()#lion的继承/执行顺序
- 避免使用菱形继承,建议通过mixin的组合完成
- 通过父类的方法,尽量使用super去调用。
多态:
1.子类的实例对象也是父类的实例对象(广义)
- isinstance(cub,lion) #判断cub是不是lion的子类
2.同一类中,有同名的函数,但参数不一样,可用通过传递不同的参数,自动去调用不同的函数,这种多态是狭义上的多态,python中没有。
二、线程,进程,协程
进程:系统资源分配的最小单位
线程:系统任务调度的最小单位
协程:又叫做微线程
资源占用 | 数据通信 | 上下文切换 | |
---|---|---|---|
进程 | 大(M) | 不方便(网络、共享内存、管道等) | 操作系统按时间片切换,不够灵活,慢 |
线程 | 小(k) | 非常方便 | 按时间片切换,不够灵活,快 |
协程 | 非常小(bytes) | 非常方便 | 根据I/O事件切换,更加有效的利用cpu |
线程:from threading import Thread
- 并行:
- 并发:
- GIL:全局解释锁
多线程直接数据共享, - 线程安全-->加锁:
lock=threading.lock()#创建一把锁 lock.acquire()#加同步锁 lock.release()#解锁
进程:
from multiprocessing import Process p=Process(target=foo,args=(('hot'))) p.start() p.join()#阻塞
进程之间资源不共享,解决:队列q.get(),管道obj.recv(),manager管理器
进程池Pool
当需要的子进程不多时,可用直接利用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,如果创建很多进程,那么操作系统会频繁的创建和销毁进程,因为创建和销毁进程需要用到大量的资源,所以这样会消耗操作系统大量的资源。此时就可以用到multiprocessing模块提供的Pool方法,创建进程的个数是电脑核数的一倍到两倍。协程: stackless/greenlets/grvrnt/tornado/asyncio
- 协程是运行再一个线程上的,可以理解为一个线程上可以运行多个请求,
- 效率高于线程
- 线程的切换操作系统要保持上下文切换的资源,
- 协程是当进行io操作时就会自动的切换,基本不会耗费资源。
gevent协程
创建协程
import gevent def run1(): print('a') gevent.sleep(2) print('b') def run1(): print('a') gevent.sleep(1) print('b') if __name__ =='__main__': gevent.joinall([ gevent.spawn(run1), gevent.spawn(run2) ])
由于是第三方库,它不会自动识别哪些操作时io操作,所以要在代码投加上猴子补丁
from gevent import monkey monkey.patch_all() - asyncio协程 import asyncio async def run1(): print('a') await asyncio.sleep(2) print('b') async def run2(): print('c') await asyncio.sleep(1)#await释放cpu,自己辩别改代码是否是进行io操作,若是,则需要加await# print('d') if __name__ == '__main__': loop = asyncio.get_event_loop()#创建一个事件循环对象# tasks = [run2(), run1()] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
三、socket网络通信
TCP:可靠的文件传输协议
UDP:不可靠的文件传输协议
#服务端 import socket #设置套接字 server=socket.socket() #绑定端口号,ip server.bind(('127.0.0.1',8888)) #设置允许连接的客户端的个数 server.listen(5) #监听端口链接 sock,_=server.accept() #接收数据,recv中要加上接收的字节,一般为1024 recv_data=sock.recv(1024) #处理完之后,把数据返回给客户端 data = 'hello %s' % recv_data.decode() sock.send(data.encode())
四、三次握手四次挥手
为什么握手时三次,挥手却是四次?
建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
tcp的2MSL:
MSL:tcp报文的最大生存时间
最后一次发送响应的一端需要等待2MSL才关闭
说明:2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,
当TCP的一端发起主动关闭,在发出最后一个ACK包后,
即第3次握 手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,
必须在此状态上停留两倍的MSL时间,
等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,
那么对方在超时后将重发第三次握手的FIN包,
主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。
当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。
不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。
取消这个2MSL等待时间
import socket server=socket.socket() server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)