包含:
- 1、利用SocketServer进行网络编程
- 2、IO多路复用 select
- 3、多线程 threading
- 4、抽象基类abc
- 5、inspect模块
1、利用SocketServer进行网络编程
网络编程最基础是 socket模块,下面介绍SocketServer模块,关于更多,参考
服务器端
#! /usr/bin/env python
#--*--coding:utf8--*--
from SocketServer import (TCPServer as TCP,StreamRequestHandler as SRH)
from time import ctime
HOST = ''
PORT = 21567
ADDR = (HOST, PORT)
class MyRequestHandle(SRH):
def handle(self):
print '...connected from :', self.client_address
self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))
tcpServer = TCP(ADDR,MyRequestHandle)
print 'waiting for connecting'
tcpServer.serve_forever()
客户端
#! /usr/bin/env python
#--*--coding:utf8--*--
from socket import *
HOST = 'localhost'
PORT = 21567
BUFFER = 1024
ADDR = (HOST,PORT)
while True:
tcpclisock = socket(AF_INET,SOCK_STREAM)
tcpclisock.connect(ADDR)
data = raw_input('>')
if not data:
break
tcpclisock.send('%s\r\n' % data)
data = tcpclisock.recv(BUFFER)
if not data:
break
print data.strip()
tcpclisock.close()
2、IO多路复用 select
程序来源:http://python.jobbole.com/84058/
import select
import socket
import sys
HOST = 'localhost'
PORT = 5000
BUFFER_SIZE = 1024
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(5)
inputs = [server, sys.stdin]
running = True
while True:
try:
# 调用 select 函数,阻塞等待
readable, writeable, exceptional = select.select(inputs, [], [])
except select.error, e:
break
# 数据抵达,循环
for sock in readable:
# 建立连接
if sock == server:
conn, addr = server.accept()
# select 监听的socket
inputs.append(conn)
elif sock == sys.stdin:
junk = sys.stdin.readlines()
running = False
else:
try:
# 读取客户端连接发送的数据
data = sock.recv(BUFFER_SIZE)
if data:
sock.send(data)
if data.endswith('\r\n\r\n'):
# 移除select监听的socket
inputs.remove(sock)
sock.close()
else:
# 移除select监听的socket
inputs.remove(sock)
sock.close()
except socket.error, e:
inputs.remove(sock)
server.close()
Linux select模块基本原理可以在高级IO中简单回顾,python中对select函数API做了简单修改,select.select(rlist, wlist, xlist[, timeout])
程序中 inputs = [server, sys.stdin]
为读监听列表,列表中是需要监听的文件描述符。当某个准备就绪时,会返回三个列表readable, writeable, exceptional
,否则一直等待。通过类似下列语句
for sock in readable:
# 建立连接
if sock == server:
轮询判断哪个描述符准备好,并进行相应操作。
select的不足
select优点之一就是跨平台的特性并且在描述符数量小时用起来很好。但也存在以下问题:
select需要遍历监视的文件描述符,并且这个描述符的数组还有最大的限制。随着文件描述符数量的增长,用户态和内核的地址空间的复制所引发的开销也会线性增长。即使监视的文件描述符长时间不活跃了,select还是会线性扫描。
为了解决这些问题,操作系统又提供了poll方案,但是poll的模型和select大致相当,只是改变了一些限制。目前Linux最先进的方式是epoll模型。
3、多线程 threading
参考来源 :http://python.jobbole.com/81546/
官方文档
两种创建线程方法:
1、继承Thread类,重写他的run方法
import threading, time, random
count = 0
class Counter(threading.Thread):
def __init__(self, lock, threadName):
'''@summary: 初始化对象。
@param lock: 琐对象。
@param threadName: 线程名称。
'''
super(Counter, self).__init__(name = threadName) #注意:一定要显式的调用父类的初始
化函数。
self.lock = lock
def run(self):
'''@summary: 重写父类run方法,在线程启动后执行该方法内的代码。
'''
global count
self.lock.acquire()
for i in xrange(10000):
count = count + 1
self.lock.release()
lock = threading.Lock()
for i in range(5):
Counter(lock, "thread-" + str(i)).start()
time.sleep(2) #确保线程都执行完毕
print count
这里要说明一下run方法 和start方法: 它们都是从Thread继承而来的,run()方法将在线程开启后执行,可以把相关的逻辑写到run方法中(通常把run方法称为活动[Activity]。);start()方法用于启动线程。
2、创建一个threading.Thread对象,在它的初始化函数(init)中将可调用对象作为参数传入
import threading, time, random
count = 0
lock = threading.Lock()
def doAdd():
'''@summary: 将全局变量count 逐一的增加10000。
'''
global count, lock
lock.acquire()
for i in xrange(10000):
count = count + 1
lock.release()
for i in range(5):
threading.Thread(target = doAdd, args = (), name = 'thread-' + str(i)).start()
time.sleep(2) #确保线程都执行完毕
print count
threading.Thread类的初始化函数原型:
def __init__(self, group=None, target=None, name=None, args=(), kwargs={})
- 参数group是预留的,用于将来扩展;
- 参数target是一个可调用对象(也称为活动[activity]),在线程启动后执行;
- 参数name是线程的名字。默认值为“Thread-N“,N是一个数字。
- 参数args和kwargs分别表示调用target时的参数列表和关键字参数
更多参考方法和属性参考 http://python.jobbole.com/81546/
条件变量
多线程中条件变量应用也很广泛,用于两个线程之间对某个共享信息之间的通信,比如线程A中等待某条件为真,该条件在线程B中改变,当成真时,发送一个信号给线程A继续运行
import threading, time
class Hider(threading.Thread):
def __init__(self, cond, name):
super(Hider, self).__init__()
self.cond = cond
self.name = name
def run(self):
time.sleep(1) #确保先运行Seeker中的方法
self.cond.acquire() #b
print self.name + ': 我已经把眼睛蒙上了'
self.cond.notify()
self.cond.wait() #c
#f
print self.name + ': 我找到你了 ~_~'
self.cond.notify()
self.cond.release()
#g
print self.name + ': 我赢了' #h
class Seeker(threading.Thread):
def __init__(self, cond, name):
super(Seeker, self).__init__()
self.cond = cond
self.name = name
def run(self):
self.cond.acquire()
self.cond.wait() #a #释放对琐的占用,同时线程挂起在这里,直到被notify并重新占有琐。
#d
print self.name + ': 我已经藏好了,你快来找我吧'
self.cond.notify()
self.cond.wait() #e
#h
self.cond.release()
print self.name + ': 被你找到了,哎~~~'
cond = threading.Condition()
seeker = Seeker(cond, 'seeker')
hider = Hider(cond, 'hider')
seeker.start()
hider.start()
注意到,在seeker函数和Hider函数中都有一个self.cond.acquire(),就是说同一把锁获得了两次,这在一般的lock对象中会发生死锁,threadiong.Condition在内部维护一个RLock对象,
两者区别是:
这两种琐的主要区别是:RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。
threading中还有一个Event方法,可以实现和条件对象类似的功能,内部维护一个标识符来实现线程间的同步问题。
threading.Event() # 初始化一个Event
Event.wait([timeout])
堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)。
Event.set()
将标识位设为Ture
Event.clear()
将标识伴设为False。
Event.isSet()
判断标识位是否为Ture。
还有个常用的模块是Timer,可以指定时间间隔执行某个操作
def hello():
print "hello, world"
t = Timer(3, hello)
t.start() # 3秒钟之后执行hello函数。
4、抽象基类abc
参考来源 http://www.jianshu.com/p/19ed49293168
https://segmentfault.com/a/1190000007921371
python中并没有提供抽象类与抽象方法,但是提供了内置模块abc(abstract base class)来模拟实现抽象类。
抽象类,顾名思义,是在更高的逻辑层面上定义了函数API,比如将动物定义为一个抽象类,定义抽象方法 奔跑,叫声等,可以不作具体实现,二是其子类做相应实现,比如狗,鸡等各自实现自己的方法。
通过@abc.abstractmethod
将方法声明为抽象方法,比如:
import abc
class PluginBase(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def load(self, input):
"""Retrieve data from the input source and return an object."""
return
@abc.abstractmethod
def save(self, output, data):
"""Save the data object to the output."""
return
具体化抽象基类有以下两种方式,
1、继承
class SubclassImplementation(PluginBase):
def load(self, input):
return input.read()
def save(self, output, data):
return output.write(data)
if __name__ == '__main__':
print 'Subclass:', issubclass(SubclassImplementation, PluginBase)
print 'Instance:', isinstance(SubclassImplementation(), PluginBase)
继承方式的优点:直接从抽象基类派生子类有一个好处,除非子类实现抽象基类的抽象方法,否则子类不能实例化。
2、注册
class RegisteredImplementation(object):
def load(self, input):
return input.read()
def save(self, output, data):
return output.write(data)
PluginBase.register(RegisteredImplementation)
if __name__ == '__main__':
print 'Subclass:', issubclass(RegisteredImplementation, PluginBase)
print 'Instance:', isinstance(RegisteredImplementation(), PluginBase)
注册方式的缺点:不会出现在类的MRO (Method Resolution Order),故而也不能通过super()来调用抽象方法。当没有实现抽象方法时,实例化时候不会报错,只有在调用时候才会报错。
值得注意的是,抽象类的注册方法抽象类中 __metaclass__ = abc.ABCMeta
,必不可少,否则报错无register方法。
注意,基类中抽象类的定义方法不会对其子类有强迫作用,比如抽象类为类方法,如下,继承子类对应方法可以是静态方法或一般方法。
class PluginBase(object):
__metaclass__ = abc.ABCMeta
@classmethod
@abc.abstractmethod
def load(cls, input):
"""Retrieve data from the input source and return an object."""
return
抽象属性
import abc
class Base(object):
__metaclass__ = abc.ABCMeta
@abc.abstractproperty
def value(self):
return 'Should never get here'
class Implementation(Base):
@property
def value(self):
return 'concrete property'
try:
b = Base()
print 'Base.value:', b.value
except Exception, err:
print 'ERROR:', str(err)
i = Implementation()
print 'Implementation.value:', i.value
输出为
ERROR: Can't instantiate abstract class Base with abstract methods value
Implementation.value: concrete property
5、inspect模块
官网说明为:
The inspect module provides several useful functions to help get information about live objects such as modules, classes, methods, functions, tracebacks, frame objects, and code objects. For example, it can help you examine the contents of a class, retrieve the source code of a method, extract and format the argument list for a function, or get all the information you need to display a detailed traceback.
There are four main kinds of services provided by this module: type checking, getting source code, inspecting classes and functions, and examining the interpreter stack.
用到的知识点:
inspect.getmembers(object[, predicate]):
参考: http://blog.csdn.net/yugongpeng_blog/article/details/45670805?readlog
这个方法是dir()的扩展版,如下所示,dir只返回一个列表,没有对应的值或函数位置
import inspect
class test():
def __init__(self):
self.name = 'Yuan'
def meth(self):
pass
test = test()
res = inspect.getmembers(test)
print res
print dir(test)
输出结果:
[('__doc__', None), ('__init__', >), ('__module__', '__main__'), ('meth', >), ('name', 'Yuan')]
['__doc__', '__init__', '__module__', 'meth', 'name']
它会将dir()找到的名字对应的属性一并返回,形如[(name, value), ...]。另外,predicate是一个方法的引用,如果指定,则应当接受value作为参数并返回一个布尔值,如果为False,相应的属性将不会返回。使用is*(如isclass等)作为第二个参数可以过滤出指定类型的属性。在ryu源码中使用为:
clses = inspect.getmembers(mod, lambda cls: (inspect.isclass(cls) and
issubclass(cls, RyuApp) and
mod.__name__ ==
cls.__module__))
对mod对象中的每个模块,属性,方法,函数等根据第二个参数进行检查,当三个判断都为真才返回。
inspect.getcallargs()
Bind the args and kwds to the argument names of the Python function or method func, as if it was called with them.
from inspect import getcallargs
>>> def f(a, b=1, *pos, **named):
... pass
>>> getcallargs(f, 1, 2, 3)
{'a': 1, 'named': {}, 'b': 2, 'pos': (3,)}
>>> getcallargs(f, a=2, x=4)
{'a': 2, 'named': {'x': 4}, 'b': 1, 'pos': ()}