包含:
- 1、利用SocketServer進(jìn)行網(wǎng)絡(luò)編程
- 2、IO多路復(fù)用 select
- 3、多線程 threading
- 4、抽象基類abc
- 5、inspect模塊
1、利用SocketServer進(jìn)行網(wǎng)絡(luò)編程
網(wǎng)絡(luò)編程最基礎(chǔ)是 socket模塊,下面介紹SocketServer模塊,關(guān)于更多,參考
服務(wù)器端
#! /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多路復(fù)用 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:
# 調(diào)用 select 函數(shù),阻塞等待
readable, writeable, exceptional = select.select(inputs, [], [])
except select.error, e:
break
# 數(shù)據(jù)抵達(dá),循環(huán)
for sock in readable:
# 建立連接
if sock == server:
conn, addr = server.accept()
# select 監(jiān)聽的socket
inputs.append(conn)
elif sock == sys.stdin:
junk = sys.stdin.readlines()
running = False
else:
try:
# 讀取客戶端連接發(fā)送的數(shù)據(jù)
data = sock.recv(BUFFER_SIZE)
if data:
sock.send(data)
if data.endswith('\r\n\r\n'):
# 移除select監(jiān)聽的socket
inputs.remove(sock)
sock.close()
else:
# 移除select監(jiān)聽的socket
inputs.remove(sock)
sock.close()
except socket.error, e:
inputs.remove(sock)
server.close()
Linux select模塊基本原理可以在高級IO中簡單回顧,python中對select函數(shù)API做了簡單修改,select.select(rlist, wlist, xlist[, timeout])
程序中 inputs = [server, sys.stdin]為讀監(jiān)聽列表,列表中是需要監(jiān)聽的文件描述符。當(dāng)某個準(zhǔn)備就緒時,會返回三個列表readable, writeable, exceptional,否則一直等待。通過類似下列語句
for sock in readable:
# 建立連接
if sock == server:
輪詢判斷哪個描述符準(zhǔn)備好,并進(jìn)行相應(yīng)操作。
select的不足
select優(yōu)點(diǎn)之一就是跨平臺的特性并且在描述符數(shù)量小時用起來很好。但也存在以下問題:
select需要遍歷監(jiān)視的文件描述符,并且這個描述符的數(shù)組還有最大的限制。隨著文件描述符數(shù)量的增長,用戶態(tài)和內(nèi)核的地址空間的復(fù)制所引發(fā)的開銷也會線性增長。即使監(jiān)視的文件描述符長時間不活躍了,select還是會線性掃描。
為了解決這些問題,操作系統(tǒng)又提供了poll方案,但是poll的模型和select大致相當(dāng),只是改變了一些限制。目前Linux最先進(jìn)的方式是epoll模型。
3、多線程 threading
參考來源 :http://python.jobbole.com/81546/
官方文檔
兩種創(chuàng)建線程方法:
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) #注意:一定要顯式的調(diào)用父類的初始
化函數(shù)。
self.lock = lock
def run(self):
'''@summary: 重寫父類run方法,在線程啟動后執(zhí)行該方法內(nèi)的代碼。
'''
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) #確保線程都執(zhí)行完畢
print count
這里要說明一下run方法 和start方法: 它們都是從Thread繼承而來的,run()方法將在線程開啟后執(zhí)行,可以把相關(guān)的邏輯寫到run方法中(通常把run方法稱為活動[Activity]。);start()方法用于啟動線程。
2、創(chuàng)建一個threading.Thread對象,在它的初始化函數(shù)(init)中將可調(diào)用對象作為參數(shù)傳入
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) #確保線程都執(zhí)行完畢
print count
threading.Thread類的初始化函數(shù)原型:
def __init__(self, group=None, target=None, name=None, args=(), kwargs={})
- 參數(shù)group是預(yù)留的,用于將來擴(kuò)展;
- 參數(shù)target是一個可調(diào)用對象(也稱為活動[activity]),在線程啟動后執(zhí)行;
- 參數(shù)name是線程的名字。默認(rèn)值為“Thread-N“,N是一個數(shù)字。
- 參數(shù)args和kwargs分別表示調(diào)用target時的參數(shù)列表和關(guān)鍵字參數(shù)
更多參考方法和屬性參考 http://python.jobbole.com/81546/
條件變量
多線程中條件變量應(yīng)用也很廣泛,用于兩個線程之間對某個共享信息之間的通信,比如線程A中等待某條件為真,該條件在線程B中改變,當(dāng)成真時,發(fā)送一個信號給線程A繼續(xù)運(yùn)行
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) #確保先運(yùn)行Seeker中的方法
self.cond.acquire() #b
print self.name + ': 我已經(jīng)把眼睛蒙上了'
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 + ': 我已經(jīng)藏好了,你快來找我吧'
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函數(shù)和Hider函數(shù)中都有一個self.cond.acquire(),就是說同一把鎖獲得了兩次,這在一般的lock對象中會發(fā)生死鎖,threadiong.Condition在內(nèi)部維護(hù)一個RLock對象,
兩者區(qū)別是:
這兩種瑣的主要區(qū)別是:RLock允許在同一線程中被多次acquire。而Lock卻不允許這種情況。注意:如果使用RLock,那么acquire和release必須成對出現(xiàn),即調(diào)用了n次acquire,必須調(diào)用n次的release才能真正釋放所占用的瑣。
threading中還有一個Event方法,可以實(shí)現(xiàn)和條件對象類似的功能,內(nèi)部維護(hù)一個標(biāo)識符來實(shí)現(xiàn)線程間的同步問題。
threading.Event() # 初始化一個Event
Event.wait([timeout])
堵塞線程,直到Event對象內(nèi)部標(biāo)識位被設(shè)為True或超時(如果提供了參數(shù)timeout)。
Event.set()
將標(biāo)識位設(shè)為Ture
Event.clear()
將標(biāo)識伴設(shè)為False。
Event.isSet()
判斷標(biāo)識位是否為Ture。
還有個常用的模塊是Timer,可以指定時間間隔執(zhí)行某個操作
def hello():
print "hello, world"
t = Timer(3, hello)
t.start() # 3秒鐘之后執(zhí)行hello函數(shù)。
4、抽象基類abc
參考來源 http://www.itdecent.cn/p/19ed49293168
https://segmentfault.com/a/1190000007921371
python中并沒有提供抽象類與抽象方法,但是提供了內(nèi)置模塊abc(abstract base class)來模擬實(shí)現(xiàn)抽象類。
抽象類,顧名思義,是在更高的邏輯層面上定義了函數(shù)API,比如將動物定義為一個抽象類,定義抽象方法 奔跑,叫聲等,可以不作具體實(shí)現(xiàn),二是其子類做相應(yīng)實(shí)現(xiàn),比如狗,雞等各自實(shí)現(xiàn)自己的方法。
通過@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)
繼承方式的優(yōu)點(diǎn):直接從抽象基類派生子類有一個好處,除非子類實(shí)現(xiàn)抽象基類的抽象方法,否則子類不能實(shí)例化。
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)
注冊方式的缺點(diǎn):不會出現(xiàn)在類的MRO (Method Resolution Order),故而也不能通過super()來調(diào)用抽象方法。當(dāng)沒有實(shí)現(xiàn)抽象方法時,實(shí)例化時候不會報錯,只有在調(diào)用時候才會報錯。
值得注意的是,抽象類的注冊方法抽象類中 __metaclass__ = abc.ABCMeta,必不可少,否則報錯無register方法。
注意,基類中抽象類的定義方法不會對其子類有強(qiáng)迫作用,比如抽象類為類方法,如下,繼承子類對應(yīng)方法可以是靜態(tài)方法或一般方法。
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模塊
官網(wǎng)說明為:
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.
用到的知識點(diǎn):
inspect.getmembers(object[, predicate]):
參考: http://blog.csdn.net/yugongpeng_blog/article/details/45670805?readlog
這個方法是dir()的擴(kuò)展版,如下所示,dir只返回一個列表,沒有對應(yīng)的值或函數(shù)位置
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)
輸出結(jié)果:
[('__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()找到的名字對應(yīng)的屬性一并返回,形如[(name, value), ...]。另外,predicate是一個方法的引用,如果指定,則應(yīng)當(dāng)接受value作為參數(shù)并返回一個布爾值,如果為False,相應(yīng)的屬性將不會返回。使用is*(如isclass等)作為第二個參數(shù)可以過濾出指定類型的屬性。在ryu源碼中使用為:
clses = inspect.getmembers(mod, lambda cls: (inspect.isclass(cls) and
issubclass(cls, RyuApp) and
mod.__name__ ==
cls.__module__))
對mod對象中的每個模塊,屬性,方法,函數(shù)等根據(jù)第二個參數(shù)進(jìn)行檢查,當(dāng)三個判斷都為真才返回。
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': ()}