0.本周知识点预览
- python作用域
- 浅谈py2和py3的多继承
- socketserver源码简析
- IO多路复用
- 初识多线程
1.python作用域
先看一个简单的例子
例子1:
def func(): name = "lk" func() print(name)
执行结果如下:
Traceback (most recent call last): File "/Users/liukai/PycharmProjects/s13/day10/test.py", line 12, in <module> print(name) NameError: name 'name' is not defined
例子2:
if 1==1: name = "lk" print(name)
执行结果如下:
lk
例子3:
name = 'lk' def f1(): print(name) def f2(): name = "liukai" return f1 ret = f2() ret()
执行结果如下:
lk
例子4:
li = [lambda :x for x in range(10)] print(li[0],li[1]) print(li[0]())
执行结果如下:
<function <listcomp>.<lambda> at 0x1079470d0> <function <listcomp>.<lambda> at 0x107947158> 9
代码解析:
例子1:变量name定义在函数func()内,当print(name)的时候,报变量未定义,可推断出,python至少作用域为函数级。
例子2:在if else代码块内,定义了变量name,可以成功print,可见,python也并不是代码块级作用域。PS:java、c#是代码块级作用域。
例子3:python为函数级作用域,而且内部函数未找到变量时,会去外层函数找,但例子3中,f1函数在f2函数内,不过输出的并不是我们想要的liukai,说明,python的作用域在代码执行之前就确定了,和函数如何嵌套是没关系的,代码从上到下执行,到加载到f1函数时,作用域就为函数体内,如若找不到变量,便去函数体外找,并不是在f2内。
例子4:li列表的最终结果为10个lambda函数,如li[0],li[1]...,不过为什么li[0]不等于0呢?这是因为,for x in range(10),已经让x等于9,并且,函数如若不加括号,便不会执行,也不会加载函数体内的内容。所有函数体内的x值一直不变,当执行li[0]()的时候,便会输出此时已经等于9的x。
综上:python的作用域为函数级,包括class,def,lambda;if else / while / for 不会改变作用域,搜索顺序为先搜索本地局部变量,在搜索上层变量直至全局变量。
2.py2和py3的多继承
1.py2
这里先说python2.7的版本。python2.7版本的类分为两种。
一、经典类
class A: def fuck(self): print("我是A类") class B(A): def haha(self): print("我是B类") class C(A): def haha(self): print("我是C类") class D(B): def haha(self): print("我是D类") class E(C): def fuck(self): print("我是E类") class F(D,E): def haha(self): print("我是F类") f = F() f.fuck()
执行结果如下:
我是A类
代码解析:python2.7的经典类的继承关系为深度优先,一直找到最顶层的父类,也就是一条道走到黑的模式。
如图:
二、新式类
class A(object): def fuck(self): print("我是A类") class B(A): def haha(self): print("我是B类") class C(A): def haha(self): print("我是C类") class D(B): def haha(self): print("我是D类") class E(C): def fuck(self): print("我是E类") class F(D,E): def haha(self): print("我是F类") f = F() f.fuck()
执行结果如下:
我是E类
代码解析:经典类和新式类在写法上的区别为 class A 和 class A (object),新式类的继承方式为广度优先。
如图:
2.py3
python3的类继承举例:
class A: def fuck(self): print("我是A类") class B(A): def haha(self): print("我是B类") class C(A): def haha(self): print("我是C类") class D(B): def haha(self): print("我是D类") class E(C): def fuck(self): print("我是E类") class F(D,E): def haha(self): print("我是F类") f = F() f.fuck()
执行结果如下:
我是E类
代码解析:python3的类继承和python2.7的新式类的继承方式一样,都是广度优先。
3.socketserver 源码简析
socketserver源码举例:
import socketserver ###导入socketserver模块 class MySocketServer(socketserver.BaseRequestHandler): ###创建一个类继承socketserver.BaseRequestHandler def handle(self): pass if __name__ == '__main__': server = socketserver.ThreadingTCPServer(("127.0.0.1",8007),MySocketServer) ###实例化socketserver.ThreadingTCPServer的对象,参数一是地址和端口,参数二是创建的类 server.serve_forever()
以下为socketserver源码的执行顺序
###找sockerserver 源码 -> socketserver.ThreadingTCPServer类 ###1. TCPServer __init__ ###2. BaseServer __init__ 封装变量 self.server_address = server_address = ("127.0.0.1",8007) ### self.RequestHandlerClass = RequestHandlerClass = MySocketServer ###3. server.serve_forever() ###4. 找BaseServer的serve_forever() serve_forever()执行self._handle_request_noblock ###5. self._handle_request_noblock 执行 self.process_request() ###6. process_request 去 ThreadingMixIn 类里找,并执行了self.process_request_thread ###7. self.process_request_thread 执行了 self.finish_request , finish_request 去BaseServer 找 ###8. finish_request 里执行了self.RequestHandlerClass(request, client_address, self) ###9. self.RequestHandlerClass(request, client_address, self) -> MySocketServer(request, client_address, self) ###10. MySocketServer(request, client_address, self) 会执行 MySocketServer 的__init__方法 ###11. MySocketServer 中没有 __init__方法,便去父类找,咱们写的父类是socketserver.BaseRequestHandler ###12. 父类中执行self.handle(),便是执行MySocketServer的handle()
4.I/O多路复用
I/O多路复用,有三种方法,select、poll、epoll。
windows适用于select
mac 适用于select
linux 适用于select、poll、epoll
在这里只举例select的方法
服务端:
#!/usr/bin/env python3 # -*- coding=utf-8 -*- # Author : LiuKai ## I/O 多路复用 import socket ###导入socket模块 import select ###导入select模块 s = socket.socket() ###创建一个socket s.bind(("127.0.0.1",9999,)) ###绑定地址 s.listen(5) ###设置等待队列 input = [s,] ###设置监听socket队列 output = [] ###设置将要发送数据的socket队列 while True: ###rlist 是当参数1 -> input中的socket发生变化时,就会将变化的socket加入到rlist中.变化是指当创建的socket接收请求(accept方法),或者创建连接的(conn方法) ###wlist 是只要参数2有值,就将socket对象传入wlist ###elist 是只要参数3的socket出现问题,就会把其加入到elist ###第四个参数1,代表超时时间,如若未设置,则select 会一直阻塞,直到文件句柄发生变化,如若设置为1,那么监听的socket无变化则阻塞1秒,返回空列表. rlist, wlist, elist = select.select(input,output,[],1) print(len(input),len(rlist),len(output),len(wlist)) ###只要有新连接或者接收消息,则rlist就会添加元素 for i in rlist: ###循环rlist,假如是新连接,则接受请求,把conn添加到input中,方便select 监听 if i == s: conn, address = i.accept() input.append(conn) conn.sendall(bytes("hello",encoding="utf-8")) ###假如不是新连接,而是接收的信息(conn变化了),则如若接收没问题,则把连接(conn)假如到output列表中,方便wlist监听,发送消息. else: try: data = i.recv(1024) if not data: raise Exception("收到空值") else: output.append(i) except: ###假如客户端断开连接造成异常,则在监听队列中删除该conn input.remove(i) ###从监听的即将要发送的等待队列中(conn),遍历该列表,发送消息,消息发出后,从发送队列中删除该conn. for j in wlist: j.sendall(bytes("response",encoding="utf-8")) output.remove(j)
客户端:
#!/usr/bin/env python3 # -*- coding=utf-8 -*- # Author : LiuKai import socket s = socket.socket() s.connect(("127.0.0.1",9999,)) data = s.recv(1024) print(data) while True: inp = input(">>>") s.sendall(bytes(inp,encoding="utf-8")) data = s.recv(1024) print(data) s.close()
执行服务端后,执行多个客户端,客户端结果如下:
b'hello' >>>ll b'response' >>>ll b'response'
服务端结果如下:
0 0 1 1 1 1 0 0 1 0 1 1 1 1 0 0 1 0 1 1 1 0 0 0
代码解析:服务端的结果为当创建一个连接,第一列和第二列为1,当新建的连接(第二列)发生变化后,连接用掉之后,会归0,当接收消息后,根据代码逻辑,会放入第三列一个socket待监听,致使第四列也会变为1.服务端发出消息后,第三列、第四列都归0.
5.初识多线程
简介:
0.线程是应用程序中工作的最小单元。
1.一个应用程序,可以有单、多进程,可以有单、多线程。
2.默认:单进程、单线程
3.当程序不怎么占用CPU时,I/O操作较频繁时,推荐用单进程、多线程。
4.当程序多进行计算性操作,利用CPU较频繁,I/O操作较少时,推荐用单线程、多进程。
5.由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。
不用多线程的代码:
import time def f1(arg): time.sleep(1) print(arg) print(time.ctime()) for i in range(5): f1(i) print(time.ctime())
执行结果如下:
Wed Jul 13 22:19:55 2016 0 1 2 3 4 Wed Jul 13 22:20:00 2016
代码解析:可见,连续执行5次函数f1,执行的时间为5秒。
以下为利用多线程的代码:
import threading import time def f1(arg): time.sleep(1) print(arg) print(time.ctime()) for i in range(5): t = threading.Thread(target=f1,args=(i,)) t.start() print(time.ctime())
执行结果如下:
Wed Jul 13 22:25:38 2016 Wed Jul 13 22:25:38 2016 0 2 3 4 1
代码解析:利用多线程,执行时间变为1秒。