0.本周知识点预览
python作用域
浅谈py2和py3的多继承
socketserver源码简析
IO多路复用
初识多线程
1.python作用域
先看一个简单的例子
例子1:
deffunc():
name= "lk"func()print(name)
执行结果如下:
Traceback (most recent call last):
File"/Users/liukai/PycharmProjects/s13/day10/test.py", line 12, in
print(name)
NameError: name'name' is not defined
例子2:
if 1==1:
name= "lk"
print(name)
执行结果如下:
lk
例子3:
name = 'lk'
deff1():print(name)deff2():
name= "liukai"
returnf1
ret=f2()
ret()
执行结果如下:
lk
例子4:
li = [lambda :x for x in range(10)]print(li[0],li[1])print(li[0]())
执行结果如下:
. at 0x1079470d0> . 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版本的类分为两种。
一、经典类
classA:deffuck(self):print("我是A类")classB(A):defhaha(self):print("我是B类")classC(A):defhaha(self):print("我是C类")classD(B):defhaha(self):print("我是D类")classE(C):deffuck(self):print("我是E类")classF(D,E):defhaha(self):print("我是F类")
f=F()
f.fuck()
执行结果如下:
我是A类
代码解析:python2.7的经典类的继承关系为深度优先,一直找到最顶层的父类,也就是一条道走到黑的模式。
如图:
二、新式类
classA(object):deffuck(self):print("我是A类")classB(A):defhaha(self):print("我是B类")classC(A):defhaha(self):print("我是C类")classD(B):defhaha(self):print("我是D类")classE(C):deffuck(self):print("我是E类")classF(D,E):defhaha(self):print("我是F类")
f=F()
f.fuck()
执行结果如下:
我是E类
代码解析:经典类和新式类在写法上的区别为 class A 和 class A (object),新式类的继承方式为广度优先。
如图:
2.py3
python3的类继承举例:
classA:deffuck(self):print("我是A类")classB(A):defhaha(self):print("我是B类")classC(A):defhaha(self):print("我是C类")classD(B):defhaha(self):print("我是D类")classE(C):deffuck(self):print("我是E类")classF(D,E):defhaha(self):print("我是F类")
f=F()
f.fuck()
执行结果如下:
我是E类
代码解析:python3的类继承和python2.7的新式类的继承方式一样,都是广度优先。
3.socketserver 源码简析
socketserver源码举例:
import socketserver ###导入socketserver模块
class MySocketServer(socketserver.BaseRequestHandler): ###创建一个类继承socketserver.BaseRequestHandler
defhandle(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队列
whileTrue:###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 inrlist:###循环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 notdata:raise Exception("收到空值")else:
output.append(i)except:###假如客户端断开连接造成异常,则在监听队列中删除该conn
input.remove(i)###从监听的即将要发送的等待队列中(conn),遍历该列表,发送消息,消息发出后,从发送队列中删除该conn.
for j inwlist:
j.sendall(bytes("response",encoding="utf-8"))
output.remove(j)
客户端:
#!/usr/bin/env python3#-*- coding=utf-8 -*-#Author : LiuKai
importsocket
s=socket.socket()
s.connect(("127.0.0.1",9999,))
data= s.recv(1024)print(data)whileTrue:
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 10 01 0 1 1
1 10 01 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条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。
不用多线程的代码:
importtimedeff1(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 201601
2
3
4Wed Jul13 22:20:00 2016
代码解析:可见,连续执行5次函数f1,执行的时间为5秒。
以下为利用多线程的代码:
importthreadingimporttimedeff1(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 2016Wed Jul13 22:25:38 201602
3
4
1
代码解析:利用多线程,执行时间变为1秒。