python學習筆記(二)

此文爲學習廖雪峯的Python學習教程的筆記,接下來是更加高級的部分,將會進入到一些實際用途很大的章節,由於本人之前有一定的java編程經驗,所以不自覺地會和java進行對比,如果沒有學過請自行忽略,不影響閱讀。如果有不恰當的地方,也歡迎進行糾正,以免誤導他人。願能有所收穫,以下是正文部分:

面向對象編程

  • 區分面向對象與面向過程:面向過程是將計算機程序視爲一系列命令的集合,即一組函數順序執行。面向對象是將計算機程序視爲一組對象的集合,對象包含數據和操作數據的方法。實際

操作時只用將類實例化爲對象並調用函數進行操作即可,而且各對象之間可以互相影響,增強了代碼的複用性和可維護性。

  • 類是抽象的模板,經過實例化纔會成爲一個個具體的對象。每個對象擁有的方法相同,但是數據可能不同。**由於Python是動態語言,因此可以在外部對對象添加屬性,這個與java很不一

樣。**

  • 創建類的格式:
    1. 通過class關鍵字定義類,之後緊跟類名,類名通常是大寫開頭的單詞,緊接着是(object),表示該類是從哪個類繼承下來的。如果沒有明確的父類,就默認爲object
    2. __init__方法可以將類需要綁定的屬性寫入。它的第一個參數永遠是self,表示創建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因爲self就指向創建的

實例本身。調用時不用寫入self,但是要將必要參數寫入。
3. 和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是實例變量self,並且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什麼區別,所以,你仍

然可以用默認參數、可變參數和關鍵字參數。
4. 要定義一個方法,除了第一個參數是self外,其他和普通函數一樣。要調用一個方法,只需要在實例變量上直接調用,除了self不用傳遞,其他參數正常傳入
5. 封裝有利於添加新的功能
6. 以下爲示例:

class Test(object):

    def __init__(self,name,age):
        self.name = name
        self.age = age

    def print_msg(self):
        print 'name: %s age: %d' % (self.name,self.age)

steve = Test('steve' , 16)
steve.print_msg()

注意:在我敲這段代碼時print_msg函數的參數忘加self導致錯誤,這是python類中的函數與一般函數不一樣的地方,切記!

訪問限制

java中私有變量可以通過private修飾符,公有變量可以通過public來聲明,但是python中如果需要聲明類的私有變量,就需要在變量前·加__修飾,當然和之前模塊的私有變量一樣,這個也是一個君子協定,仍然有方法調用私有變量,方法就是xxx,我們還是儘量做君子,不要使用這種方式爲好,畢竟這樣既不規範也不安全。
示例:

def __init__(self,name,age):
        self.__name = name
        self.__age = age

    def print_msg(self):
        print 'name: %s age: %d' % (self.__name,self.__age)

當需要訪問和更改私有變量時,寫訪問器和更改器即可。例:

 def get_name(self):
    return self.__name
def set_age(self, age):
    self.__age = age

繼承與多態

提到面向對象編程就肯定繞不開這兩個特性,這是OOP三板斧中的兩個(另一個就是之前提到的封裝)。java中通過extend來表示繼承關係,但是在python中繼承很簡單:

class Teacher(People):
...

括號裏就是父類,括號外就是子類。前面也提到過,沒有可繼承的父類時,就用object填入。

多態表明的是子類同樣也是父類,此例中指老師同時也是人,當一個方法需要People作爲參數時,Teacher也可以作爲參數傳入,用java來描述就是父類的引用變量可以指向子類對象。

獲取對象信息

type()函數

>>> type(123)
<type 'int'>
>>> type('str')
<type 'str'>
  • type()函數返回的是type類型,而所有的基本類型都屬於type類型。
  • 如果一個變量指向函數或者類,也可以用type()判斷:
>>> type(abs)
<type 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>

Python把每種type類型都定義好了常量,放在types模塊裏,使用之前,需要先導入:

>>> import types
>>> type('abc')==types.StringType
True
>>> type(u'abc')==types.UnicodeType
True
>>> type([])==types.ListType
True
>>> type(str)==types.TypeType
True
  • 有一種類型就叫TypeType,所有類型本身的類型就是TypeType,比如:
>>> type(int)==type(str)==types.TypeType
True

isinstance()函數

  • isinstance()判斷的是一個對象是否是該類型本身,或者位於該類型的父繼承鏈上。
  • 能用type()判斷的基本類型也可以用isinstance()判斷:
>>> isinstance('a', str)
True
>>> isinstance(u'a', unicode)
True
>>> isinstance('a', unicode)
False
  • 還可以判斷一個變量是否是某些類型中的一種,比如下面的代碼就可以判斷是否是str或者unicode:
>>> isinstance('a', (str, unicode))
True
>>> isinstance(u'a', (str, unicode))
True

由於str和unicode都是從basestring繼承下來的,所以,還可以把上面的代碼簡化爲:

>>> isinstance(u'a', basestring)
True

dir()函數

如果要獲得一個對象的所有屬性和方法,可以使用dir()函數,它返回一個包含字符串的list,比如,獲得一個str對象的所有屬性和方法:

>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
  • 操作對象的屬性:getattr()setattr()以及hasattr(),分別可用於獲取屬性,設置屬性和查詢屬性是否存在。具體實例參考獲取對象信息

面向對象高級編程

使用__slots__

由於python是一門動態語言,允許在調用的過程中添加屬性,但如果給Student添加不被允許的方法或屬性就會導致一些不必要的麻煩,因此這時需要__slots__屬性來限制類的屬性添加,例如:

>>> class Student(object):
...     __slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱
...

此時如果動態添加除name和age外的屬性,程序就會報錯,但是這條規則對子類無效,因爲子類的__slots__包含的內容除了父類的還有自己的。

@property

一個getter方法變成屬性,只需要加上@property就可以了,此時,@property本身又創建了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值,例如:

class Test(object):

    @property
    def name(self):
        return self._name
    @name.setter
    def name(self,val):
        self._name = val

    @property
    def age(self):
        return self._age

多重繼承

如果一個類具有多個父類的屬性,可以通過多繼承來實現。多重繼承的實現只需要直接在括號中添加其他父類即可:

class Student(People,PlayGame)
  • Mixin
    由於繼承關係主線一般都是單鏈,例如學生繼承自人,但是玩遊戲也是需要額外添加的功能。因此爲了更好地確立關係,需要引入Mixin,這是一種用於明確表明額外功能而非父類(此例中就是學生的父類爲人,但是可以加入玩遊戲的功能),此時需要進行一點改進:
class Student(People,PlayGameMixin)

定製類

  • __str__()
    當直接print對象時,就會調用__str__方法,對其進行重定義即可獲得自己定製的字符串。

  • __repr__()
    返回程序開發者看到的字符串,也就是說,__repr__()是爲調試服務的。當在交互界面直接輸入對象的引用時就會調用此方法。

  • __iter__()
    如果一個類想被用於for … in循環,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代對象,然後,Python的for循環就會不斷調用該迭代對象的next()方法拿到循環的下一個值,直到遇到StopIteration錯誤時退出循環。

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化兩個計數器a,b

    def __iter__(self):
        return self # 實例本身就是迭代對象,故返回自己

    def next(self):
        self.a, self.b = self.b, self.a + self.b # 計算下一個值
        if self.a > 100000: # 退出循環的條件
            raise StopIteration();
        return self.a # 返回下一個值

調用實例作用於for循環:

>>> for n in Fib():
...     print n
...
1
1
2
3
5
...
46368
  • __getitem__()
    用於使引用變量可以像list那樣取每一個值。例如:
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

利用類似下標訪問:

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3

– 切片對象slice
加上slice對象判斷,可以對引用變量使用切片:

class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice):
            start = n.start
            stop = n.stop
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  • __getattr__()
    當本類中沒有相關屬性時,會調用此方法查找,因此它可以用於存放額外變量。

  • __call__()
    本方法可以用於將實例直接調用,而不只是通過調用方法來操作數據。

  • callable()函數
    通過此函數可判斷一個對象是否可調用,如:

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('string')
False

錯誤處理及調試

錯誤處理

  • try...except...finally可用於異常處理,當執行出錯時執行except後面的代碼,except後可添加else,當沒有異常時執行。finally後的代碼是無論有無異常均會執行,這與java的try…catch…finally相似。

  • raise可以將異常拋出,與java中throws相似。不過它可以更改拋出的異常類型,這點比較特殊。

調試

  1. print 這個就是在你需要知道某個變量值的地方打印一下就行,簡單粗暴,不利於後期清除。
  2. assert 斷言,這個相當於設了斷點,錯誤時拋出AssertionError,內容就是你的第二個字符串參數。如:assert n != 0, 'n is zero!'
  3. logging 它允許你指定記錄信息的級別,有debug,info,warning,error等幾個級別,當我們指定level=INFO時,logging.debug就不起作用了。同理,指定level=WARNING後,debug和info就不起作用了。這樣一來,你可以放心地輸出不同級別的信息,也不用刪除,最後統一控制輸出哪個級別的信息。
  4. pdb 通過python -m pdb file_name.py啓動(file_name是你的python文件名,此處只是在正常執行語句中間加了-m pdb)常用操作有:

    • l 列出代碼

    • p 變量名 查看變量值

    • n 執行下一行代碼

    • q 結束調試,退出程序

  5. pdb.set_trace() 設置斷點:

import pdb

s = '0'
n = int(s)
pdb.set_trace() # 運行到這裏會自動暫停
print 10 / n

運行代碼,程序會自動在pdb.set_trace()暫停並進入pdb調試環境,可以用命令p查看變量,或者用命令c繼續運行.關於pdb的一些命令

IO編程

  1. 使用內置的open()函數,通過read()可以一次讀取所有內容,通過write()可以寫入文件。
>>> f = open('e:/test.txt', 'r')
>>> f.read()
'Hello, world!'
>>> f.write('test')
>>> f.close()

2.用with... as...來簡化代碼,可以免去每次打開完文件後關閉文件,自動用完後調用close():

with open('/path/to/file', 'r') as f:
    print f.read()

3.codecs可用於自動轉換成相應編碼:

import codecs
with codecs.open('e:/gbk.txt', 'r', 'gbk') as f:
    f.read()

4.read(size)方法,每次最多讀取size個字節的內容。類似於java中的buffer.直接調用read()會讀取全部內容。
5.調用readline()可以每次讀取一行內容,調用readlines()一次讀取所有內容並按行返回list。
6.file-like Object :像open()函數返回的這種有個read()方法的對象,在Python中統稱爲file-like Object。除了file外,還可以是內存的字節流,網絡流,自定義流等等。file-like Object不要求從特定類繼承,只要寫個read()方法就行。

序列化

  • 我們把變量從內存中變成可存儲或傳輸的過程稱之爲序列化,在Python中叫pickling。序列化之後,就可以把序列化後的內容寫入磁盤,或者通過網絡傳輸到別的機器上。反過來,把變量內容從序列化的對象重新讀到內存裏稱之爲反序列化,即unpickling。

  • Python提供兩個模塊來實現序列化:cPicklepickle。這兩個模塊功能是一樣的,區別在於cPickle是C語言寫的,速度快,pickle是純Python寫的,速度慢,跟cStringIOStringIO一個道理。用的時候,先嚐試導入cPickle,如果失敗,再導入pickle:

try:
    import cPickle as pickle
except ImportError:
    import pickle
  • pickle.dumps()方法把任意對象序列化成一個str,然後,就可以把這個str寫入文件。或者用另一個方法pickle.dump()直接把對象序列化後寫入一個file-like Object:
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
"(dp0\nS'age'\np1\nI20\nsS'score'\np2\nI88\nsS'name'\np3\nS'Bob'\np4\ns."

>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
  • 如果我們要在不同的編程語言之間傳遞對象,就必須把對象序列化爲標準格式,比如XML,但更好的方法是序列化爲JSON,因爲JSON表示出來就是一個字符串,可以被所有語言讀取,也可以方便地存儲到磁盤或者通過網絡傳輸。
  • 把Python對象變成一個JSON:
>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'

dumps()方法返回一個str,內容就是標準的JSON。

  • 反序列化得到的所有字符串對象默認都是unicode而不是str。由於JSON標準規定JSON編碼是UTF-8,所以我們總是能正確地在Python的str或unicode與JSON的字符串之間轉換。

多進程與多線程

多進程

由於Python是跨平臺的,自然也應該提供一個跨平臺的多進程支持。multiprocessing模塊就是跨平臺版本的多進程模塊。

  • multiprocessing模塊提供了一個Process類來代表一個進程對象,下面的例子演示了啓動一個子進程並等待其結束:
from multiprocessing import Process
import os

# 子進程要執行的代碼
def run_proc(name):
    print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
    print 'Parent process %s.' % os.getpid()
    p = Process(target=run_proc, args=('test',))
    print 'Process will start.'
    p.start()
    p.join()
    print 'Process end.'

執行結果如下:

Parent process 17896.
Process will start.
Run child process test (16660)...
Process end
  • 創建子進程時,只需要傳入一個執行函數和函數的參數,創建一個Process實例,用start()方法啓動。
    join()方法可以等待子進程結束後再繼續往下運行,通常用於進程間的同步。

Pool

如果要啓動大量的子進程,可以用進程池的方式批量創建子進程:

from multiprocessing import Pool
import os , time, random

def long_time_task(name):
    print 'Run task %s (%s) ...' % (name, os.getpid())
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print 'Task %s runs %0.2f seconds. ' % (name, (end - start))

if __name__ == '__main__' :
    print 'Parent process %s.' % os.getpid()
    p = Pool()
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print 'Waiting for all subprocesses done ...'
    p.close()
    p.join()
    print 'All subprocess done.'

結果如下:

Parent process 24112.
Waiting for all subprocesses done ...
RRRun task 1 (43460) ...un task 0 (1032) ...
Run task 2 (27968) ...
un task 3 (52252) ...

Task 0 runs 0.25 seconds.
Run task 4 (1032) ...
Task 1 runs 0.81 seconds.
Task 2 runs 1.65 seconds.
Task 4 runs 2.17 seconds.
Task 3 runs 2.46 seconds.
All subprocess done.

注意:pool默認大小爲電腦的核數,如果要改爲默認一次執行10個,則可寫成p=Pool(10)

進程間通信

  • multiprocessing模塊包裝了底層的機制,提供了Queue、Pipes等多種方式來交換數據。
from multiprocessing import Process, Queue
import os , time, random

def write(q):
    for value in ['A' , 'B' , 'C']:
        print 'Put %s to queue...' % value
        q.put(value)
        time.sleep(random.random())

def read(q):
    while True:
        value = q.get(True)
        print 'Get %s from queue.' % value

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args = (q,))
    pr = Process(target=read, args = (q,))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()

結果如下:

from multiprocessing import Process, Queue
import os , time, random

def write(q):
    for value in ['A' , 'B' , 'C']:
        print 'Put %s to queue...' % value
        q.put(value)
        time.sleep(random.random())

def read(q):
    while True:
        value = q.get(True)
        print 'Get %s from queue.' % value

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args = (q,))
    pr = Process(target=read, args = (q,))
    pw.start()
    pr.start()
    pw.join() #等待pw結束
    pr.terminate() #pr是死循環,必須強行終止

多線程

Python的標準庫提供了兩個模塊:threadthreading,thread是低級模塊,threading是高級模塊,對thread進行了封裝。絕大多數情況下,我們只需要使用threading這個高級模塊。

  • 啓動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行:
import time, threading

def loop():
    print 'thread %s is running...' % threading.current_thread().name
    n = 0
    while n < 5:
        n = n + 1
        print 'thread %s >>> %s' % (threading.current_thread().name, n)
        time.sleep(1)
    print 'thread %s ended' % threading.current_thread().name

print 'thread %s is running...' %  threading.current_thread().name
t = threading.Thread(target = loop, name = 'LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name

結果如下:

thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended
thread MainThread ended.

由於任何進程默認就會啓動一個線程,我們把該線程稱爲主線程,主線程又可以啓動新的線程,Python的threading模塊有個current_thread()函數,它永遠返回當前線程的實例。主線程實例的名字叫MainThread,子線程的名字在創建時指定,我們用LoopThread命名子線程。名字僅僅在打印時用來顯示,完全沒有其他意義,如果不起名字Python就自動給線程命名爲Thread-1,Thread-2……

Lock

多線程和多進程最大的不同在於,多進程中,同一個變量,各自有一份拷貝存在於每個進程中,互不影響,而多線程中,所有變量都由所有線程共享,所以,任何一個變量都可以被任何一個線程修改,因此,線程之間共享數據最大的危險在於多個線程同時改一個變量,把內容給改亂了。
用法示例:

balance = 0
lock = threading.Lock()

def run_thread(n):
    for i in range(100000):
        # 先要獲取鎖:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要釋放鎖:
            lock.release()

當多個線程同時執行lock.acquire()時,只有一個線程能成功地獲取鎖,然後繼續執行代碼,其他線程就繼續等待直到獲得鎖爲止。
注:此處try...finally用於保證即使有異常也要釋放鎖,防止其他線程持續等待。

因爲Python的線程雖然是真正的線程,但解釋器執行代碼時,有一個GIL鎖:Global Interpreter Lock,任何Python線程執行前,必須先獲得GIL鎖,然後,每執行100條字節碼,解釋器就自動釋放GIL鎖,讓別的線程有機會執行。這個GIL全局鎖實際上把所有線程的執行代碼都給上了鎖,所以,多線程在Python中只能交替執行,即使100個線程跑在100核CPU上,也只能用到1個核。

python多線程無法利用多核的優勢,就是由於GIL的存在,不過也不用擔心,利用多進程可以避免這種問題。

ThreadLocal

爲了解決多線程之間數據不方便共享,創建局部變量層層傳遞又太麻煩的問題,引入了ThreadLocal:

import threading

# 創建全局ThreadLocal對象:
local_school = threading.local()

def process_student():
    print 'Hello, %s (in %s)' % (local_school.student, threading.current_thread().name)

def process_thread(name):
    # 綁定ThreadLocal的student:
    local_school.student = name
    process_student()

t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()

結果如下:

Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)

全局變量local_school就是一個ThreadLocal對象,每個Thread對它都可以讀寫student屬性,但互不影響。你可以把local_school看成全局變量,但每個屬性如local_school.student都是線程的局部變量,可以任意讀寫而互不干擾,也不用管理鎖的問題,ThreadLocal內部會處理。

網絡編程

TCP編程

客戶端:

# -*- coding: utf-8 -*-

# 導入socket
import socket
import codecs
# 創建一個socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立連接
s.connect(('www.baidu.com', 80))

# 發送數據
s.send('GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n')
# 接收數據
buffer = []
while True:
    #每次最多接受1K字節

    d = s.recv(1024).decode('utf-8')
    if d:
        buffer.append(d)
    else:
        break

data = ''.join(buffer)

#關閉連接
s.close()

header, html = data.split('\r\n\r\n', 1)
print header
# 把接收的數據寫入文件
with codecs.open('baidu.html', 'w' , 'utf-8') as f:
    f.write(html)
  • 創建Socket時,AF_INET指定使用IPv4協議,如果要用更先進的IPv6,就指定爲AF_INET6。SOCK_STREAM指定使用面向流的TCP協議,這樣,一個Socket對象就創建成功,但是還沒有建立連接。

  • 每種小於1024的端口都有固定作用。80用於網絡服務。

  • connect()的參數是一個元組,包括服務器IP和端口號。

  • 接收數據時,調用recv(max)方法,一次最多接收指定的字節數,因此,在一個while循環中反覆接收,直到recv()返回空數據,表示接收完畢,退出循環。

當我們接收完數據後,調用close()方法關閉Socket,這樣,一次完整的網絡通信就結束了。

服務端:
服務器進程首先要綁定一個端口並監聽來自其他客戶端的連接。如果某個客戶端連接過來了,服務器就與該客戶端建立Socket連接,隨後的通信就靠這個Socket連接了。

所以,服務器會打開固定端口(比如80)監聽,每來一個客戶端連接,就創建該Socket連接。由於服務器會有大量來自客戶端的連接,所以,服務器要能夠區分一個Socket連接是和哪個客戶端綁定的。一個Socket依賴4項:服務器地址、服務器端口、客戶端地址、客戶端端口來唯一確定一個Socket。

但是服務器還需要同時響應多個客戶端的請求,所以,每個連接都需要一個新的進程或者新的線程來處理,否則,服務器一次就只能服務一個客戶端了。

import socket,threading,time

# 創建一個socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 監聽端口
s.bind(('127.0.0.1' , 9999))

s.listen(5)
print 'Waiting fro connection...'

def tcplink(sock, addr):
    print 'Accept new connection from %s:%s...' % addr
    sock.send('Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if data == 'exit' or not data:
            break
        sock.send('Hello, %s!' % data)
    sock.close()
    print 'Connection from %s:%s closed.' % addr

while True:
    # 接受一個新連接:
    sock , addr = s.accept()
    # 創建新線程處理TCP連接:
    t = threading.Thread(target=tcplink, args = (sock, addr))
    t.start()

UDP編程

UDP的使用與TCP類似,但是不需要建立連接。此外,服務器綁定UDP端口和TCP端口互不衝突,也就是說,UDP的9999端口與TCP的9999端口可以各自綁定。

服務器端:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 綁定端口:
s.bind(('127.0.0.1', 9999))
print 'Bind UDP on 9999...'
while True:
    # 接收數據:
    data, addr = s.recvfrom(1024)
    print 'Received from %s:%s.' % addr
    s.sendto('Hello, %s!' % data, addr)

recvfrom()方法返回數據和客戶端的地址與端口,這樣,服務器收到數據後,直接調用sendto()就可以把數據用UDP發給客戶端。SOCK_DGRAM代表用UDP方式傳輸。

客戶端:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in ['Michael', 'Tracy', 'Sarah']:
    # 發送數據:
    s.sendto(data, ('127.0.0.1', 9999))
    # 接收數據:
    print s.recv(1024)
s.close()

此處通過sendto發送,recv接收.


以上是關於python面向對象,IO,多線程,異常處理,網絡編程的基礎內容,其他內容後面有機會再補充。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章