包含:
- 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.lxweimin.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__', <bound method test.__init__ of <__main__.test instance at 0x01E9C288>>), ('__module__', '__main__'), ('meth', <bound method test.meth of <__main__.test instance at 0x01E9C288>>), ('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': ()}