python学习之路-10 网络编程之进阶
本篇介绍内容
- 作用域
- python类的多继承
- IO多路复用
- socketserver之源码剖析
- 多线程和多进程
作用域
if 1 == 1:
name = "xxx"
print(name)
上面代码在python和javascript中没有块级作用域,是可以执行的但是在c#和java中有块级作用域,会报错变量name不存在
python的作用域链
- 由内向外找
- 找不到就报错
当程序还没执行作用域在执行之前就已经定义好了,下面的例子证明了这点
name = "aaa"
def f2():
name = "bbb"
def f1():
print(name)
f1()
f2() # 在f2执行之前f1和f2的作用域就已经被定义好了
# 输出
bbb
name = "aaa"
def f1():
print(name)
def f2():
name = "bbb"
f1()
f2() # 在f2执行之前f1和f2的作用域就已经被定义好了
# 输出
aaa
name = "aaa"
def f1():
print(name)
def f2():
name = "bbb"
return f1
# 在f2函数被执行之前f1和f2的作用域就已经被定义好了
ret = f2()
ret()
# 输出
aaa
li = [lambda:x for x in range(10)]
上面的语句等同于下面的
li = []
for x in range(10):
def f1():
return x
li.append(f1)
print(li[0]())
多继承
-
python3
-
python2.7
- 经典类
- 新式类 继承object
IO多路复用
select poll epoll
IO多路复用底层就是监听socket对象内部是否有变化,是否在收发消息
-
select IO多路复用之监听客户端连接
- 服务端
import socket import select sk = socket.socket() sk.bind(("127.0.0.1", 9999,)) sk.listen(5) while True: rlist, w, e = select.select([sk, ], [], [], 5) # 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk] # 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端] print(rlist) for r in rlist: print(r) conn, address = r.accept() conn.sendall(bytes("hello", encoding="utf-8"))
- 客户端
import socket sk = socket.socket() sk.connect(("127.0.0.1", 9999,)) data = sk.recv(1024) print(data) while True: input(">>") sk.close()
-
select IO多路复用之监听客户端连接以及接收客户端发送的数据
- 服务端
import socket import select sk = socket.socket() sk.bind(("127.0.0.1", 9999,)) sk.listen(5) inputs = [sk,] while True: rlist, w, e = select.select(inputs, [], [], 1) # 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk] # 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端] print(len(inputs), len(rlist)) for r in rlist: print(r) if r == sk: # 有新的客户端连接进来 conn, address = r.accept() inputs.append(conn) conn.sendall(bytes("hello", encoding="utf-8")) else: # 有客户端给我发送数据 recv_data = r.recv(1024) print(recv_data)
- 客户端
import socket sk = socket.socket() sk.connect(("127.0.0.1", 9999,)) data = sk.recv(1024) print(data) while True: inp = input(">>") sk.sendall(bytes(inp, encoding='utf-8')) sk.close()
-
select IO多路复用之读写分离
- 服务端
import socket import select sk = socket.socket() sk.bind(("127.0.0.1", 9999,)) sk.listen(5) inputs = [sk,] outputs = [] while True: rlist, wlist, e = select.select(inputs, outputs, [], 1) # 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk] # 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端] print(len(inputs), len(rlist)) for r in rlist: print(r) if r == sk: # 有新的客户端连接进来 conn, address = r.accept() inputs.append(conn) conn.sendall(bytes("hello", encoding="utf-8")) else: # 有客户端给我发送数据 try: recv_data = r.recv(1024) if not recv_data: # 某些操作系统,在socket断开的时候,会发送一个空给server,所以要判断空的情况 raise Exception("断开连接") else: outputs.append(r) except Exception as e: inputs.remove(r) for w in wlist: w.sendall(bytes("response", encoding="utf-8")) outputs.remove(w)
- 客户端
import socket sk = socket.socket() sk.connect(("127.0.0.1", 9999,)) data = sk.recv(1024) print(data) while True: inp = input(">>") sk.sendall(bytes(inp, encoding='utf-8')) print(sk.recv(1024)) sk.close()
-
select IO多路复用之读写分离 - 服务端根据客户端的输入返回对应的信息
- 服务端
import socket import select sk = socket.socket() sk.bind(("127.0.0.1", 9999,)) sk.listen(5) inputs = [sk, ] # outputs = [] messages = {} # 用来存放客户端发送过来的消息,存放格式 连接客户端的对象: 消息 while True: rlist, wlist, elist = select.select(inputs, outputs, [sk, ], 1) # rlist 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk] # wlist 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端] # elist 监听sk(服务器端)对象是否发生错误 print(len(inputs), len(rlist)) for r in rlist: print(r) if r == sk: # 有新的客户端连接进来 conn, address = r.accept() inputs.append(conn) messages[conn] = [] # 在存储消息的字典中创建一个以客户端连接对象为key 空列表为value的键值对 conn.sendall(bytes("hello", encoding="utf-8")) else: # 有客户端给我发送数据 try: recv_data = r.recv(1024) if not recv_data: # 某些操作系统,在socket断开的时候,会发送一个空给server,所以要判断空的情况 raise Exception("断开连接") else: outputs.append(r) messages[r].append(recv_data) except Exception as e: inputs.remove(r) del messages[r] for w in wlist: print(messages[w]) msg = messages[w].pop() resp = msg + bytes("response", encoding="utf-8") w.sendall(resp) outputs.remove(w)
- 客户端
import socket sk = socket.socket() sk.connect(("127.0.0.1", 9999,)) data = sk.recv(1024) print(data) while True: inp = input(">>") sk.sendall(bytes(inp, encoding='utf-8')) print(sk.recv(1024)) sk.close()
socketserver之源码剖析
多线程和多进程
多线程和多进程的定义
- 一个应用程序,可以有多进程和多线程
- 默认:单进程,单线程
- 单进程,多线程
- IO类型的操作,不占用CPU,使用多线程进行IO操作,提高并发
- 计算类型的操作,需要CPU计算,使用多进程进行计算型密集的操作提高并发,可以利用上CPU多核的特性
- GIL全局解释器锁,在java和c#中是不存在GIL的,多线程也可以达到并发
多线程
-
初识多线程
- 当没有多线程的时候只能够像下面代码中的这种方式进行工作
import time def renwu(renwu_id): time.sleep(1) print(renwu_id) if __name__ == '__main__': for i in range(10): renwu(i)
- 使用多线程进行操作
import time import threading def renwu(renwu_id): time.sleep(1) print(renwu_id) if __name__ == '__main__': for i in range(10): t = threading.Thread(target=renwu, args=[i,]) t.start()
-
多线程模块下面Thread类的一些方法
getName() # 获得当前线程对象的名称
ident # 返回一个非0的整数 线程标识符 未运行的返回值为空
is_alive() # 返回一个布尔值,判断线程的存活状态
isDaemon() # 返回一个布尔值,判断线程是否为后台程序
join() # 设置子线程为阻塞模式
setDaemon(daemonic) # 设置对象是否为后台程序,该方法需要在start方法之前,运行需要一个参数,传True或者False
setName(name) # 设置线程的名称
start() # 执行线程,不代表当前线程会被立即执行,什么时候执行由cpu调度决定
- 模块threading下面的一些函数
threading.active_count() # 返回当前存活的线程数量,等于threading.enumerate()返回列表的长度
threading.current_thread() # 返回当前主线程对象
threading.enumerate() # 返回一个列表,该列表包括当前处于存活状态的线程对象
threading.get_ident() # 返回一个非0的整数 线程标识符