python进阶到高阶大全(强烈推荐)
?
關鍵字is 和 == 的區別
a = 'hello world' b = 'hello world' a == b #返回True a is b #返回False注意:is 判斷是否是一個ID, == 判斷內容是否一致。
深拷貝和淺拷貝
import copy a = [1,2,3,4,5] b = a #淺拷貝,a,b同時指向一個id,當其中一個修改時,另外一個也會被修改。 c = copy.deepcopy(a) #深拷貝,c單獨開辟一個id,用來存儲和a一樣的內容。 d =a[:] #這樣也是深拷貝。 e = copy.copy(a) #當拷貝內容是可變類型時,那么就會進行深拷貝,如果是不可變類型時,那么就會進行淺拷貝。注意:深拷貝指的是復制內容,單獨開辟一個內存,淺拷貝指的是兩個變量同時指向一個內存ID。
私有化和Property
class Test(object):def __init__(self):self.__num = 100@getNum.setter #等同步于 porperty(setNum,getNum)def setNum(self,num): #將self.__num的屬性封裝。self.__num = num@porperty #等于getNum = porperty(getNum) 默認的是getter方法。def getNum(self) #獲取__num的值。return self.__numnum = porperty(getNum,setNum) #使用關鍵字porperty將getNum和setNum方法打包使用,并將引用賦予屬性num。
t = Test()
print(t.__num) #將會出錯,表示輸出私有屬性,外部無法使用。
t.__num = 200 #這里將會理解為添加屬性 __num = 200,而不是重新賦值私有屬性。
print(t.__num) #這里輸出的200是定義的屬性__num,而不是self.__num。
t.setNum(200) #通過set方法將私有屬性重新賦值。
t.getNum() #通過get方法獲取__num的值。
print(_Test__num) #私有屬性其實是系統再私有屬性前加上了一個_Test,就是一個下劃線加類名。t.num = 300 #調用類屬性num,并重新賦值,porperty會自動檢測set方法和get方法,并將引用賦值給set方法。
print(t.num) #輸出類屬性,并會自己檢測使用get方法進行輸出。
注意: num 前后沒有下劃線的是公有方法,_num 前邊有一個下劃線的為私有方法或屬性,子類無法繼承, 前邊有兩個下劃線的 一般是為了避免于子類屬性或者方法名沖突,無法在外部直接訪問。前后都有雙下劃線的為系統方法或屬性。后邊單個下劃線的可以避免與系統關鍵詞沖突。
列表生成式
range(1,100,5) #第一個參數表示開始位,第二個參數表示結束位(不含),第三個參數表示步長,就是每5個數返回一次。 a = [i for i in range(1,10)] #列表生成式表示返回i的值,并且返回9次,每次返回的是i的值。 a = [2 for i in range(1,10)] #這里表示返回2,并且返回9次,但是每次的值都是2。 a = [i for i in range10 if i%2==0] #表示在生成式內部加入if判斷,當i除以2的余數等于0的時候將數值返回。 a = [(i,j) for i in range(5) for j in range(5)] #表示將i和j的值以元組為元素的形式返回,當i循環一次的時候j循環5次,以此類推。生成器
a = (i for i in range(1,10)) #將列表生成試外部的中括號改為小括號,就能將生成式轉化為生成器。 next(a),a.__next__() #生成器的取值方式只能使用next的方法。 def num():a,b = 0,1for i in range(10):yield b #生成關鍵字yield,有yield的關鍵字的代碼塊就是yield的生成器。當運行到yield時代碼就會停止,并返回運行結果,當在次運行時依舊是到yield停止,并返回結果。 切記:生成器只能使用next方法。a,b = b,a+btemp = yield b #這里并不是變量的定義,當運行到yield時就會停止,所以當運行到等號右邊的時候就會停止運行,當在次使用next的時候,將會把一個None賦值給temp,因為b的值已經在上輪循環中輸出。這里可以使用num().send()方法將一個新的值賦值給temp。 a = num() #將生成器賦值給變量a。 for n in a: #生成器可以使用for循環使用,并且不會出錯。print(n)注意:生成器占用內存小,在使用的時候取值,降低CPU和內存空間,提高效率。并且一般都使用for循環進行取值。
迭代器
for i in '',[],(),{},{:} #可以for循環的對象是可迭代對象。 a = (x for i in range(100)) #列表生成式,把中括號改為小括號就可以變為一個列表生成器,是可迭代對象。 from collections import Iterable #如果想驗證是否是可迭代對象,可以使用isinstance()判斷是否是可迭代對象。 isinstance('abc',Ierable) #判斷語法 a = [1,2,3,4,5] b = iter(a) #使用iter()方法可以將可迭代對象轉換為可迭代對象。注意:生成器是可迭代對象,迭代器不一定是生成器。并且迭代器無法回取,只能向前取值。
注意:一個對象具有 iter 方法的才能稱為可迭代對象,使用yield生成的迭代器函數,也有iter方法。凡是沒有iter方法的對象不是可迭代對象,凡是沒有__next__()方法的不是是生成器。(這里的方法都是魔法方法,是內置方法,可以使用dir()查看)
閉包
def num(num): #定義函數def num_in(nim_in): #定義函數return num + num_in #返回兩個參數的和。return num_in #返回內部函數的引用。(變量名)a = num(100) #將參數為100的函數num接收,并賦值給a,只不過這個返回值是一個函數的引用。等于 a = num_in,注意這里接收的不光是函數本身,還有已經傳遞的參數。 b = a(100) #調用函數a,即num_in,并傳遞一個參數100,返回值給b。注意:當一個函數定義在另一個函數內,且使用到了外部函數的參數。整個代碼塊稱為閉包。當外部參數確定時,內部函數參數可以反復調用。
裝飾器
裝飾沒有參數的函數
def function(func): #定義了一個閉包def func_in(): #閉包內的函數print('這里是需要裝飾的內容,就是需要添加的內容')func() #調用實參函數。return func_indef test(): #需要被裝飾修改的函數。print('無參函數的測試')test = function(test) #裝飾器的原理就是將原有的函數名重新定義為以原函數為參數的閉包。 test() 這里再次掉用test()的時候,其實是將會調用閉包內的函數func_in()。所以將會起到裝飾修改的作用,最后會再次調用原函數test()。@function #裝飾器的python寫法,等價于test = function(test),并且無需調用當代碼運行道這里,Python會自動運行。 def test():print('無參函數的測試') test() #這里再次調用函數時,將會產生修改后的效果。裝飾帶有參數的函數
def function(func): #定義了一個閉包def func_in(*args,**kwargs): #閉包內的函數,因為裝飾器運行的實則是閉包內的函數,所以這里將需要有形參用來接收原函數的參數。print('這里是需要裝飾的內容,就是需要添加的內容')func(*args,**kwargs) #調用實參函數,并傳入一致的實參。return func_in@function #裝飾器的python寫法,等價于test = function(test) . def test():print('無參函數的測試')test(5,6) #這里再次掉用test()的時候,其實是將會調用閉包內的函數func_in()。所以將會起到裝飾修改的作用,最后會再次調用原函數test()。裝飾帶有返回值的函數
def function(func): #定義了一個閉包def func_in(*args,**kwargs): #閉包內的函數,因為裝飾器運行的實則是閉包內的函數,所以這里將需要有形參用來接收原函數的參數。print('這里是需要裝飾的內容,就是需要添加的內容')num = func(*args,**kwargs) #調用實參函數,并傳入一致的實參,并且用變量來接收原函數的返回值,return num #將接受到的返回值再次返回到新的test()函數中。return func_in @function def test(a,b): #定義一個函數return a+b #返回實參的和通用裝飾器
def function(func): #定義了一個閉包def func_in(*args,**kwargs): #閉包內的函數,因為裝飾器運行的實則是閉包內的函數,所以這里將需要有形參用來接收原函數的參數。print('這里是需要裝飾的內容,就是需要添加的內容')num = func(*args,**kwargs) #調用實參函數,并傳入一致的實參,并且用變量來接收原函數的返回值,return num #將接受到的返回值再次返回到新的test()函數中。return func_in帶有參數的裝飾器
def func(*args,**kwags):def function(func): #定義了一個閉包def func_in(*args,**kwargs): #閉包內的函數,因為裝飾器運行的實則是閉包內的函數,所以這里將需要有形參用來接收原函數的參數。print('這里是需要裝飾的內容,就是需要添加的內容')num = func(*args,**kwargs) #調用實參函數,并傳入一致的實參,并且用變量來接收原函數的返回值,return num #將接受到的返回值再次返回到新的test()函數中。return func_inreturn function@func(50) #這里會先運行函數func,并切傳入參數,之后會再次運行閉包函數進行裝飾, @func(50)>>@function,然后將由@function繼續進行裝飾修改。 def test(a,b):print('這是一個函數')return a+b- ?
動態語言添加屬性和方法
class Person(): #創建一個類def __init__(self,name): #定義初始化信息。self.name = name li = Person('李') #實例化Person('李'),給變量li li.age = 20 #再程序沒有停止下,將實例屬性age傳入。動態語言的特點。 Person.age = None #這里使用類名來創建一個屬性age給類,默認值是None。Python支持的動態屬性添加。 def eat(self): #定義一個方法,不過這個方法再類之外。print('%s正在吃東西。。'%self.name) import types #動態添加方法需要使用tpyes模塊。 li.eat = types.MethodType(eat,li) #使用types.MethodType,將函數名和實例對象傳入,進行方法綁定。并且將結果返回給li.eat變量。實則是使用一個和li.eat方法一樣的變量名用來調用。 li.eat() #調用外部方法eat()方法。@staticmethod #定義靜態方法。 def test(): #定義靜態方法,靜態方法可以不用self參數。print('這是一個靜態方法。') Person.test = test #使用類名.方法名 = test的形式來方便記憶和使用,Person.test其實只是一個變量名,沒有特殊的含義。 Person.test() #調用test方法。@classmethod #類方法 def test(cls): print('這是一個類方法。') Person.test = test #定義一個類屬性等于方法名。 Person.test() #調用方法。class test(object): #定義一個類。__slots__ = ('name','age') #使用slots來將屬性固定,不能進行動態添加修改。元類
創建帶有類屬性的類
Test = type('Test',(object,),{'num':0} #元類是只使用type創建的類,使用type會有3個參數,第一個是類名,第二個小括號內是父類名,需要使用元組。第三個字典中是類屬性,使用type能夠快速的動態創建一個類。 class Test(object): #創建一個類,等價于上邊num = 0創建帶有方法的類
def eat(self): #定義一個函數,self作為第一個參數。print ('%s正在吃飯。。'%self.name) Person = type('Person',(object,), {'eat':eat,'name':None} #使用type創建一個類,但是有兩個屬性,一個是eat,一個是name,但是eat的值是函數eat的引用。 p = Person() #實例化 p.name = 'Tom' #類屬性賦值 p.eat() #調用eat()方法。內建屬性
__init__ #構造初始化函數,__new__之后運行 __new__ #創建實例所需的屬性 __class__ #實例所在的類,實例.__class__ __str__ #實例的字符串表示,可讀性高 __repr__ #實例的字符串表示,準確性高 __del__ #刪除實例引用 __dict__ #實力自定義屬性,vars(實例.__dict__) __doc__ #類文檔,help(類或者實例) __bases__ #當前類的所有父類 __getattribute__ #屬性訪問攔截器。內建方法
range(start,stop,[,step]) #生成器 map(function, iterable, ...) # map() 會根據提供的函數對指定序列做映射。 filter(function, iterable) #filter() 函數用于過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表。 reduce(function, iterable[, initializer]) #reduce() 函數會對參數序列中元素進行累積。 sorted(iterable[, cmp[, key[, reverse]]]) #sorted() 函數對所有可迭代的對象進行排序操作。sort 與 sorted 區別: sort 是應用在 list 上的方法,sorted 可以對所有可迭代的對象進行排序操作。 list 的 sort 方法返回的是對已經存在的列表進行操作,而內建函數 sorted 方法返回的是一個新的 list,而不是在原來的基礎上進行的操作。PDB調試
1.python -m pdb xxx.py #在命令行輸入以上命令,進入pdb調試模式。XXX.py表示需要打開的文件。 2.import pdb pdb.run('func(*args)') #第二種方式,當程序在運行中調試。 3.pdb.set_trace() #第三種方法,當程序運行到這行代碼時,就會自動運行。 l(list) # 顯示全部代碼 n(next) # 向下執行一行代碼 c(contiune) # 執行余下的代碼 b(break) 10 # 設置斷點,b 10表示將斷點設置到第10行。clear 1,刪除第一個斷點 p(print) a,b #打印變量的值 a(args) #打印全部的形參數據 s(step) #進入到一個函數 r(return) #快速執行到函數的最后一行進程和線程
進程
import os pid = os.fork() #這里將會創建一個子進程,返回值會是子進程PID值。 print('父子進程都會輸出。') #這里沒有判斷語句,將會運行兩次,一次是父進程,一次是子進程。 if pid > 0: #判斷,父進程的返回值會大于0。print('子進程的PID是%d,父進程的PID是%d'%(os.getpid(),os.getppid())) #getpid的獲取當前進程的pid,如果子進程getpid的時候,會得到子進程的值,再子進程使用getppid的時候能夠獲取到父進程的pid。 else: #子進程的返回值則會永遠是0print('父進程的PID是%d'%os.getpid()) #當父進程使用getpid的時候獲得的是父進程的pid。**注意:**進程值PID是不能重復的,類似于端口。系統會為每個進程會分配不同的PID進行區分不同的軟件進程。并且父子進程會獨立運行,互不干擾。而且父子進程的調用需要系統來調度,沒有固定性。
import os pid = os.fork() #創建子進程,接收pid的返回值。 if pid > 0: #判斷是子進程還是父進程。print('父進程') #當pid的返回值是0的時候,會運行父進程 else:print('子進程') #否則就是子進程 pid =os.fork() #讓之前的父子進程再次創建各自的子進程 if pid > 0: #判斷父子進程print('父進程的子進程') #這里會運行2次父進程 else:print('子進程的子進程') #這里也會運行兩次子進程windons中的fork()-Process
from multiprocessing import Process #導入模塊類,這是一個類 import time def test(): #定義一個函數while True:print('-1-')time.sleep(1) p = Process(target=test) #創建一個實例,就是一個新進程,并且執行的代碼就是test()函數 p.start() #調用start方法讓子進程開始運行。 p.join(10) #join表示延時時間,也就是等待子進程的時間,當10秒過了以后,則會運行主進程。 while True: #這里是主進程。print('-2-')time.sleep(1)注意:Process需要自己創建進程,以及調用開始進程,fork則是全自動運行。后期最好以Process為主,可實現跨平臺運行。還有最主要的一點是Process的主進程會等待子進程。
Process實例
from multiprocessing import Process import timeclass Process_class(Process): #創建一個Process的子類。def run(self): #重寫run方法,當調用start方法時,則會默認調用run方法,所以不用再填寫target參數。while True:print('--1--')time.sleep(1) p = Process_class() #實例化一個子進程。 p.start() #運行子進程 p.join(5) #這里將會等待子進程單獨運行5秒。 while True: #主進程,當join等待結束收,則會父子進程一起運行。但是如果當父進程運行完,子進程還沒有結束,那么父進程會繼續等子進程。print('--main--')time.sleep(1)進程池Pool
from multiprocessing import Pool #導入Pool模塊類 import os,time def work(num): #創建一個進程的工作函數。for i in range(2): #表示每次工作需要執行2次。print('進程的pid是%d,進程值是%d'%(os.getpid(),num)) #輸出兩次time.sleep(1)p = Pool(2) #實例化對象,參數2表示創建2個子進程,就是說每次只能執行2個進程。for i in range(6): print('--%d--'%i)p.apply_async(work,(i,)) #向實例對象添加6次任務,就是6個進程,但是實例對象的進程池只有2個,需要每次執行2個進程,當2個進程執行完以后則會再次執行下面2個。p.close() #關閉進程池,不再接收進程任務。 p.join() #當子進程工作結束后,則會運行主進程。Queue隊列
Process的Queue用法
from multiprocessing import Process,Queue #導入Process和Queue import os,time,randomdef write(q): #定義函數,接收Queue的實例參數for v in range(10):print('Put %s to Queue'%v)q.put(v) #添加數據到Queuetime.sleep(1) def read(q): #定義函數,接收Queue的實例參數while True:if not q.empty(): #判斷,如果Queue不為空則進行數據取出。v = q.get(True) #取出Queue中的數據,并返回保存。print('Get %s from Queue'%v)time.sleep(1)else: #如果Queue內沒有數據則退出。breakif __name__ == '__main__':q = Queue() #實例化Queue括號內可選填,輸入數字表示有多少個存儲單位。以堵塞方式運行。必須等里邊有空余位置時,才能放入數據,或者只能等里邊有數據時才能取出數據,取不出數據,或者存不進數據的時候則會一直在等待狀態。pw = Process(target=write,args=(q,)) #實例化子進程pw,用來執行write函數,注意這里的函數不帶括號,只是傳遞引用,參數需要使用args參數以元組的方式進行接收。pr = Process(target=read,args=(q,)) #實例化子進程pr,用來執行read函數,注意這里的函數不帶括號,只是傳遞引用,參數需要使用args參數以元組的方式進行接收。pw.start() #開始執行pw。pr.start() #開始執行pr。pw.join() #等待pw結束pr.join() #等待pr結束print('Over') #主進程結束Pool的Queue用法
from multiprocessing import Manager,Pool #這里注意導入的是Manager和Pool import os,time,randomdef write(q):for v in range(10):print('Put %s to Queue'%v)q.put(v)time.sleep(1) def read(q):while True:if not q.empty():v = q.get(True)print('Get %s from Queue'%v)time.sleep(1)else:breakif __name__ == '__main__':q = Manager().Queue() #這里實例化的時候是使用Manager的Queuep = Pool()p.apply_async(write,(q,)) #將任務加入Pool的進程池,注意這里的參數于Process不同。p.apply_async(read,(q,)) #將任務加入Pool的進程池,注意這里的參數于Process不同。p.close() #關閉進程池,不再接收進程。p.join() #子進程完畢,運行以下的主進程。print('Over')線程
from threading import Thread #導入Thread線程類。 import timenum = 0 #定義全局變量def work(): #定義函數內容global num for i in range(1000000):num += 1print('work的num是%d'%num)def works(): #定義函數global numfor i in range(1000000):num += 1print('works的num是%d'%num)t = Thread(target=work) #創建第一個線程內置的self.name屬性為Thread-1,并指向work tt = Thread(target=works) #創建第二個線程內置的self.name屬性為Thread-2,并指向works t.start() #開始執行 tt.start() #開始執行 time.sleep(1) #主線程休息一秒 print('最后的num值是%d'%num) #輸出最后的結果。注意:線程中的變量數據是可以共享的,進程與線程的區別在于,父子進程是兩個單獨的個體,子進程類似于直接拷貝的一份父進程的代碼獨立運行,相當于兩個文件。線程則是再主進程的內部分裂運行。舉例子來說一個工廠需要做100萬件衣服,但是工期太緊,自己做太慢,老板現在有兩個選擇,一個是雇傭另外一個同樣規模的工廠一起來做,兩個工廠一起做——進程,另外一個選擇就是在自己的工廠內大批量的招募工人用來趕工——線程。總得來說線程的消耗成本會比進程低很多。
互斥鎖
from threading import Thread,Lock #導入互斥鎖Locknum = 0def work():global numl.acquire() #這里表示調用互斥鎖上鎖方法,如果work函數先運行l.acquire的話,那么后邊的程序就不能再修改和使用變量num。直到將其解鎖后才能使用。for i in range(1000000):num += 1print('work的num是%d'%num)l.release() #這里表示調用互斥鎖解鎖方法。def works():global numl.acquire() #這里表示調用互斥鎖上鎖方法。for i in range(1000000):num += 1print('works的num是%d'%num)l.release() #這里表示調用互斥鎖解鎖方法。l = Lock() #實例化互斥鎖,互斥鎖是為了保護子線程不爭搶數據而使用的一個類。 t = Thread(target=work) tt = Thread(target=works) t.start() tt.start() print('最后的num值是%d'%num) #輸出最后的結果,如果實驗過的可能會發現這個結果并不是2000000,為什么呢? 這里需要明白,主線程和子線程是同時進行的,因為創建子進程在前,最后輸出再后,所以當最后線程輸出的時候,子線程還在運行,也就是說當子線程的加法運算加到95222的時候你的 主進程剛好運行到最后的輸出語句,所以就把95222拿過來進行輸出。你也可以試試將最后的輸出語句放到實例化的前邊,看看結果是不是0,因為子線程還沒有開始工作,所以并沒有進行加法運算。注意:因為線程的數據是共享數據,不用Queue就能實現,所以也會存在一些弊端,因為線程是在進程間獨立運行的,所以共享數據會有一定的延時性和不準確性,舉例家里有10個饅頭,2個孩子,第一個孩子拿走一個會記得還剩下9個,第二個孩子去拿的時候會記得還剩下8個,但是當第一個孩子再去拿的時候會發現只剩下7個了,但是之前明明還剩下9個,這樣就會出現問題。互斥鎖的作用就是再廚房裝上一把鎖,當第一個孩子餓的時候就進去吃饅頭,將門反鎖,這樣第二個孩子就吃不到再門口等著,當第一個吃飽的時候第二個再進去,也把門鎖上。這樣一個一個的來避免沖突。
同步、異步
import threading import timeclass MyThread(threading.Thread):def run(self):global num time.sleep(1)if mutex.acquire(1): num = num+1msg = self.name+' set num to '+str(num)print msgmutex.release() num = 0 mutex = threading.Lock() def test():for i in range(5):t = MyThread()t.start() if __name__ == '__main__':test()- ?
- ?
注意:這里就是一個簡單的同步,使用互斥鎖來實現,因為每個線程在創建運行的時候都是各自做各自的,如果沒有互斥鎖來約束步調,那么結果是1,2,3,4,5的概率是未知數,但是加上了互斥鎖以后,就會對線程的運行順序進行排隊,達到預期的結果。而異步則是各個線程獨立運行,誰先做完就休息,不用等待。
threadlocal
import threading #導入模塊l = threading.local() #實例化local,注意這個local和Lock互斥鎖的名稱不同。def work(name): #創建函數l.name = name #將參數name傳遞給local實例對象的name屬性。注意:這里的l.name是創建的對象屬性。works() #調用work函數def works(): #創建函數name = l.nameprint('hello,%s,線程的name是%s'%(name,threading.current_thread().name))t1 = threading.Thread(target=work,args=('小李',)) #實例化線程對象,并調用work,參數name是小李。 t2 = threading.Thread(target=work,args=('小王',))#實例化線程對象,并調用work,參數name是小王。 t1.start() t2.start() t1.join() t2.join()- ?
注意:threadlocal是比較方便的共享數據處理辦法,他的內部類似于一個字典,Thread.name作為Key,對應的屬性作為Value,當Thread-1儲存和取值的時候,對應的是它的值,從而避免多個線程對共有數據造成錯誤和丟失。
網絡編程
Tcp/Ip協議
早期的計算機網絡,都是由各廠商自己規定一套協議,IBM、Apple和Microsoft都有各自的網絡協議,互不兼容為了把全世界的所有不同類型的計算機都連接起來,就必須規定一套全球通用的協議,為了實現互聯網這個目標,互聯網協議簇(Internet ProtocolSuite)就是通用協議標準。
因為互聯網協議包含了上百種協議標準,但是最重要的兩個協議是TCP和IP協議,所以,大家把互聯網的協議簡稱TCP/IP協議
端口
知名端口
知名端口是眾所周知的端口號,范圍從0到1023
例如:
80端口分配給HTTP服務
21端口分配給FTP服務
一般情況下,如果一個程序需要使用知名端口的需要有root權限
動態端口
動態端口的范圍是從1024到65535
之所以稱為動態端口,是因為它一般不固定分配某種服務,而是動態分配。
動態分配是指當一個系統進程或應用程序進程需要網絡通信時,它向主機申請一個端口,主機從可用的端口號中分配一個供它使用。當這個進程關閉時,同時也就釋放了所占用的端口號。
小結
端口有什么作用?在兩臺計算機通信時,只發 IP 地址是不夠的,因為同一臺計算機上跑著多個網絡程序。一個 IP 包來了之后,到底是交給瀏覽器還是 QQ,就需要端口號來區分。每個網絡程序都向操作系統申請唯一的端口號,這樣,兩個進程在兩臺計算機之間建立網絡連接就需要各自的 IP 地址和各自的端口號。
Socket-套接字
udp-套接字
from socket import * #導入socket from threading import * #導入threadingudp = socket(AF_INET,SOCK_DGRAM) #創建套接字,基于UDP傳輸協議。相對于TCP比較快。AF_INET表示使用IPV4進行鏈接。如果使用IPV6則把參數修改為AF_INET6udp.bind(('',8080)) #綁定任意ip,和8080端口,如果不進行綁定,那么每創建一個套解字就會使用一個動態端口。sendip = input('輸入接收方的IP:') sendport = int(input('輸入接收方的端口:'))def sendinfo(): #定義發送函數while True:senddata = input('請輸入發送的內容:')udp.sendto(senddata.encode('utf-8'),(sendip,sendport)) #調用套解字的sendto方法,第一個參數為編碼后的數據,第二個參數為接收方的IP和端口。def receiveinfo(): #定義接收函數while True:recvdata = udp.recvfrom(1024) #調用recvfrom方法進行數據接收,并且以元祖的方式返回,第一個參數是數據,第二個參數為IP和端口。與發送格式一致。print(recvdata[1],recvdata[0].decode('utf-8')) #將接收到的數據進行打印,并將數據進行解碼。def main():ts = Thread(target=sendinfo) #創建一個線程運行發送函數。tr = Thread(target=receiveinfo) #創建一個線程運行接收函數。ts.start()tr.start()ts.join()tr.join()if __name__ == '__main__':main()注意:socket套接字是用來再網絡間通信的模塊。
tcp-套接字
tcp-套接字 服務器
from socket import * #導入套接字tcp = socket(AF_INET,SOCK_STREAM) #創建tcp套接字tcp.bind(('',8800)) #綁定ip,和端口,客戶端需要連接這個ip和端口進行服務器連接。tcp.listen(5) #tcp監聽,參數為可連接的數量。newsocket,addr = tcp.accept() #接收客戶端的連接,并返回一個新的socket和客戶端地址。阻塞程序等待客戶端的接入。while 1: # 表示while True,只要條件類型不是空類型、0和None的False類型則就表示while True。socketDate = newsocket.recv(1024) #接收客戶端的數據。if len(socketDate)>0: #如果接收數據的長度大于0,則打印出接收到的信息,如果接收的數據長度為0,則表示客戶端使用close方法關閉了套接字。print(socketDate.decode('utf-8')) #將接收數據解碼為utf-8輸出else: #如果客戶端關閉了套接字,則跳出循環breaksendDate = input('請輸入要回復的內容:') #輸入需要回復的數據newsocket.send(sendDate.encode('utf-8')) #使用send將數據編碼為utf-8回復newsocket.close() #關閉與客戶端通信的套接字。 tcp.close() #關閉服務器的套接字,關閉后將不會再接收客戶端的連接。注意:在linux系統中listen的參數可以忽略,因為系統會自動按照內核進行最大連接數的操作,即使填寫參數也沒有效果,但是windons和mac中則會有效。以上是單線程案例。
tcp-套接字 客戶端
from socket import * #導入模塊csocket = socket(AF_INET,SOCK_STREAM) #創建套接字serverIp = input('請輸入服務器的IP:') csocket.connect((serverIp,8800)) #連接服務器while 1:sendData = input('請輸入需要發送打內容:') #輸入發送的內容csocket.send(sendData.encode('utf-8')) #編碼發送recvData = csocket.recv(1024)print('recvData:%s'%recvData.decode('utf-8')) #解碼輸出csocket.close() #關閉套接字注意:正常的編程工作中,會優先使用tcp套接字。
交換機、路由器
交換機:
轉發過濾:當?個數據幀的?的地址在MAC地址表中有映射時,它被轉發到連接?的節點的端??不是所有端?(如該數據幀為?播幀則轉發?所有端?)
學習功能:以太?交換機了解每?端?相連設備的MAC地址,并將地址同相應的端?映射起來存放在交換機緩存中的MAC地址表中
交換機能夠完成多個電腦的鏈接每個數據包的發送都是以?播的形式進?的,容易堵塞?絡如果PC不知?標IP所對應的的MAC,那么可以看出,pc會先發送arp?播,得到對?的MAC然后,在進?數據的傳送當switch第?次收到arp?播數據,會把arp?播數據包轉發給所有端?(除來源端?);如果以后還有pc詢問此IP的MAC,那么只是向?標的端?進?轉發數據。
路由器
路由器(Router)?稱?關設備(Gateway)是?于連接多個邏輯上分開的?絡所謂邏輯?絡是代表?個單獨的?絡或者?個??。當數據從?個??傳輸到另?個??時,可通過路由器的路由功能來完成具有判斷?絡地址和選擇IP路徑的功能
不在同??段的pc,需要設置默認?關才能把數據傳送過去 通常情況下,都會把路由器默認?關當路由器收到?個其它?段的數據包時,會根據“路由表”來決定,把此數據包發送到哪個端?;路由表的設定有靜態和動態?法每經過?次路由器,那么TTL值就會減1
網段、ARP、DNS、MAC地址
網段
網段(network segment)一般指一個計算機網絡中使用同一物理層設備(傳輸介質,中繼器,集線器等)能夠直接通訊的那一部分。例如,從192.168.0.1到192.168.255.255這之間就是一個網段。
A類IP段 0.0.0.0 到127.255.255.255 A類的默認子網掩碼 255.0.0.0 一個子網最多可以容納1677萬多臺電腦
B類IP段 128.0.0.0 到191.255.255.255 B類的默認子網掩碼 255.255.0.0 一個子網最多可以容納6萬臺電腦
C類IP段 192.0.0.0 到223.255.255.255 C類的默認子網掩碼 255.255.255.0 一個子網最多可以容納254臺電腦
局域網保留地址:
A類:10.0.0.0/8 10.0.0.0-10.255.255.255
B類:172.16.0.0/12 172.16.0.0-172.31.255.255
C類:192.168.0.0/16 192.168.0.0~192.168.255.255
注意:C類地址必須前三位一致的才算是一個局域網,可以不使用路由器進行通信,例如192.168.1.1-192.168.1.254 是一個局域網,B類地址則必須前兩位一致才算是一個局域網。以此類推。即子網掩碼有幾位相同的則需要有幾位一致的。
ARP
地址解析協議,即ARP(Address Resolution Protocol),是根據IP地址獲取物理地址的一個TCP/IP協議。主機發送信息時將包含目標IP地址的ARP請求廣播到網絡上的所有主機,并接收返回消息,以此確定目標的物理地址;收到返回消息后將該IP地址和物理地址存入本機ARP緩存中并保留一定時間,下次請求時直接查詢ARP緩存以節約資源。地址解析協議是建立在網絡中各個主機互相信任的基礎上的,網絡上的主機可以自主發送ARP應答消息,其他主機收到應答報文時不會檢測該報文的真實性就會將其記入本機ARP緩存;由此攻擊者就可以向某一主機發送偽ARP應答報文,使其發送的信息無法到達預期的主機或到達錯誤的主機,這就構成了一個ARP欺騙。ARP命令可用于查詢本機ARP緩存中IP地址和MAC地址的對應關系、添加或刪除靜態對應關系等。相關協議有RARP、代理ARP。NDP用于在IPv6中代替地址解析協議。
工作過程
主機A的IP地址為192.168.1.1,MAC地址為0A-11-22-33-44-01;
主機B的IP地址為192.168.1.2,MAC地址為0A-11-22-33-44-02;
當主機A要與主機B通信時,地址解析協議可以將主機B的IP地址(192.168.1.2)解析成主機B的MAC地址,以下為工作流程:
第1步:根據主機A上的路由表內容,IP確定用于訪問主機B的轉發IP地址是192.168.1.2。然后A主機在自己的本地ARP緩存中檢查主機B的匹配MAC地址。
第2步:如果主機A在ARP緩存中沒有找到映射,它將詢問192.168.1.2的硬件地址,從而將ARP請求幀廣播到本地網絡上的所有主機。源主機A的IP地址和MAC地址都包括在ARP請求中。本地網絡上的每臺主機都接收到ARP請求并且檢查是否與自己的IP地址匹配。如果主機發現請求的IP地址與自己的IP地址不匹配,它將丟棄ARP請求。
第3步:主機B確定ARP請求中的IP地址與自己的IP地址匹配,則將主機A的IP地址和MAC地址映射添加到本地ARP緩存中。
第4步:主機B將包含其MAC地址的ARP回復消息直接發送回主機A。
第5步:當主機A收到從主機B發來的ARP回復消息時,會用主機B的IP和MAC地址映射更新ARP緩存。本機緩存是有生存期的,生存期結束后,將再次重復上面的過程。主機B的MAC地址一旦確定,主機A就能向主機B發送IP通信了。
DNS
DNS服務器是(Domain Name System或者Domain Name Service)域名系統或者域名服務,域名系統為Internet上的主機分配域名地址和IP地址。用戶使用域名地址,該系統就會自動把域名地址轉為IP地址。域名服務是運行域名系統的Internet工具。執行域名服務的服務器稱之為DNS服務器,通過DNS服務器來應答域名服務的查詢。
MAC地址
MAC(Media Access Control或者Medium Access Control)地址,意譯為媒體訪問控制,或稱為物理地址、硬件地址,用來定義網絡設備的位置。在OSI模型中,第三層網絡層負責 IP地址,第二層數據鏈路層則負責 MAC地址。因此一個主機會有一個MAC地址,而每個網絡位置會有一個專屬于它的IP地址。
MAC(Medium/Media Access Control)地址,用來表示互聯網上每一個站點的標識符,采用十六進制數表示,共六個字節(48位)。其中,前三個字節是由IEEE的注冊管理機構RA負責給不同廠家分配的代碼(高位24位),也稱為"編制上唯一的標識符"(Organizationally Unique Identifier),后三個字節(低位24位)由各廠家自行指派給生產的適配器接口,稱為擴展標識符(唯一性)。一個地址塊可以生成2個不同的地址。MAC地址實際上就是適配器地址或適配器標識符EUI-48[1]
注意:在真正的信息傳輸中,發送者的ip和接收方的ip和數據包內容是不變的,期間會通過各個路由器的mac地址進行傳輸。簡單可以理解為,在網上買了一件衣服,包裹的發送方是商家(可以理解為發送者的IP),包裹的接收方是自己(理解為接收者的IP),期間的各個快遞中轉站就可以理解為各個路由器的mac地址,最后由數據將會傳遞到自己手中。
TCP3次握手、4次揮手和10種狀態
TCP3次握手
在TCP/IP協議中,TCP協議提供可靠的連接服務,采用三次握手建立一個連接.
第一次握手:建立連接時,客戶端發送syn包(syn=j)到服務器,并進入SYN_SEND狀態,等待服務器確認; SYN:同步序列編號(Synchronize Sequence Numbers)
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手.
完成三次握手,客戶端與服務器開始傳送數據
關閉連接(四次揮手)
第一次揮手:客戶端發送FIN+ACK包(序號為seq=a,確認序號ack=b)給服務端,用來關閉客戶端到服務端的數據傳送,客戶端進入FIN_WAIT_1狀態。
第二次揮手:服務端收到FIN+ACK包后,發送ACK包給客戶端進行確認,服務端進入CLOSE_WAIT狀態。客戶端收到ACK包后進入FIN_WAIT_2狀態。到這里,關閉一個單向通道。
第三次揮手:服務端發送FIN+ACK包給客戶端,服務端進入LAST_ACK狀態。
第四次揮手:客戶端收到FIN+ACK包后,發送ACK包給服務端進行確認,客戶端進入TIME_WAIT狀態,在等待30秒(可修改)后進入CLOSED狀態。服務端收到ACK包后進入CLOSED狀態,關閉另一個單向通道。
TCP十種狀態
CLOSED:表示關閉狀態(初始狀態)。
LISTEN:該狀態表示服務器端的某個SOCKET處于監聽狀態,可以接受連接。
SYN_SENT:這個狀態與SYN_RCVD遙相呼應,當客戶端SOCKET執行CONNECT連接時,它首先發送SYN報文,隨即進入到了SYN_SENT狀態,并等待服務端的發送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已發送SYN報文。
SYN_RCVD: 該狀態表示接收到SYN報文,在正常情況下,這個狀態是服務器端的SOCKET在建立TCP連接時的三次握手會話過程中的一個中間狀態,很短暫。此種狀態時,當收到客戶端的ACK報文后,會進入到ESTABLISHED狀態。
ESTABLISHED:表示連接已經建立。
FIN_WAIT_1: FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。區別是: FIN_WAIT_1狀態是當socket在ESTABLISHED狀態時,想主動關閉連接,向對方發送了FIN報文,此時該socket進入到FIN_WAIT_1狀態。 FIN_WAIT_2狀態是當對方回應ACK后,該socket進入到FIN_WAIT_2狀態,正常情況下,對方應馬上回應ACK報文,所以FIN_WAIT_1狀態一般較難見到,而FIN_WAIT_2狀態可用netstat看到。
FIN_WAIT_2:主動關閉鏈接的一方,發出FIN收到ACK以后進入該狀態。稱之為半連接或半關閉狀態。該狀態下的socket只能接收數據,不能發。
TIME_WAIT: 表示收到了對方的FIN報文,并發送出了ACK報文,等2MSL后即可回到CLOSED可用狀態。如果FIN_WAIT_1狀態下,收到對方同時帶 FIN標志和ACK標志的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。
CLOSE_WAIT: 此種狀態表示在等待關閉。當對方關閉一個SOCKET后發送FIN報文給自己,系統會回應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,察看是否還有數據發送給對方,如果沒有可以 close這個SOCKET,發送FIN報文給對方,即關閉連接。所以在CLOSE_WAIT狀態下,需要關閉連接。
LAST_ACK: 該狀態是被動關閉一方在發送FIN報文后,最后等待對方的ACK報文。當收到ACK報文后,即可以進入到CLOSED可用狀態。
tcp第十一種狀態:
CLOSING:這種狀態較特殊,屬于一種較罕見的狀態。正常情況下,當你發送FIN報文后,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文后,并沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什么情況下會出現此種情況呢?如果雙方幾乎在同時close一個SOCKET的話,那么就出現了雙方同時發送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接。
TCP的2MSL
2MSL即兩倍的MSL,TCP的TIME_WAIT狀態也稱為2MSL等待狀態,
當TCP的?端發起主動關閉,在發出最后?個ACK包后,即第3次握 ?完成后發送了第四次握?的ACK包后就進?了TIME_WAIT狀態,必須在此狀態上停留兩倍的MSL時間,等待2MSL時間主要?的是怕最后?個 ACK包對?沒收到,那么對?在超時后將重發第三次握?的FIN包,主動關閉端接到重發的FIN包后可以再發?個ACK應答包。
在TIME_WAIT狀態 時兩端的端?不能使?,要等到2MSL時間結束才可繼續使?。當連接處于2MSL等待階段時任何遲到的報?段都將被丟棄。不過在實際應?中可以通過設置 SO_REUSEADDR選項達到不必等待2MSL時間結束再使?此端?。
TCP?連接和短連接
短鏈接
?連接
常見的網絡攻擊
DDOS攻擊
注意:簡單的理解DDOS攻擊就是使用TCP的三次握手協議,編寫代碼使用多線程或者多進程方式惡意的不發送第三次握手導致服務器listen隊列爆滿,使正常的客戶無法正常連接。
DNS攻擊
DNS欺騙就是攻擊者冒充域名服務器的一種欺騙行為。 原理:如果可以冒充域名服務器,然后把查詢的IP地址設為攻擊者的IP地址,這樣的話,用戶上網就只能看到攻擊者的主頁,而不是用戶想要取得的網站的主頁了,這就是DNS欺騙的基本原理。DNS欺騙其實并不是真的"黑掉"了對方的網站,而是冒名頂替、招搖撞騙罷了。
ARP攻擊
ARP攻擊就是通過偽造IP地址和MAC地址實現ARP欺騙,能夠在網絡中產生大量的ARP通信量使網絡阻塞,攻擊者只要持續不斷的發出偽造的ARP響應包就能更改目標主機ARP緩存中的IP-MAC條目,造成網絡中斷或中間人攻擊。
ARP攻擊主要是存在于局域網網絡中,局域網中若有一臺計算機感染ARP木馬,則感染該ARP木馬的系統將會試圖通過“ARP欺騙”手段截獲所在網絡內其它計算機的通信信息,并因此造成網內其它計算機的通信故障。
攻擊者向電腦A發送一個偽造的ARP響應,告訴電腦A:電腦B的IP地址192.168.0.2對應的MAC地址是00-aa-00-62-c6-03,電腦A信以為真,將這個對應關系寫入自己的ARP緩存表中,以后發送數據時,將本應該發往電腦B的數據發送給了攻擊者。同樣的,攻擊者向電腦B也發送一個偽造的ARP響應,告訴電腦B:電腦A的IP地址192.168.0.1對應的MAC地址是00-aa-00-62-c6-03,電腦B也會將數據發送給攻擊者。
至此攻擊者就控制了電腦A和電腦B之間的流量,他可以選擇被動地監測流量,獲取密碼和其他涉密信息,也可以偽造數據,改變電腦A和電腦B之間的通信內容。
客戶端:
?
1.Python面向對象
創建類
使用class語句來創建一個新類,class之后為類的名稱并以冒號結尾,如下實例:
class ClassName:'類的幫助信息' #類文檔字符串class_suite #類體 實例: class Employee:'所有員工的基類'empCount = 0def __init__(self, name, salary):self.name = nameself.salary = salaryEmployee.empCount += 1def displayCount(self):print "Total Employee %d" % Employee.empCountdef displayEmployee(self):print "Name : ", self.name, ", Salary: ", self.salary"創建 Employee 類的第一個對象" emp1 = Employee("Zara", 2000) "創建 Employee 類的第二個對象" emp2 = Employee("Manni", 5000) emp1.displayEmployee() emp2.displayEmployee() print "Total Employee %d" % Employee.empCount輸出結果如下:
Name : Zara ,Salary: 2000 Name : Manni ,Salary: 5000 Total Employee 2 類的私有屬性:__private_attrs:兩個下劃線開頭,聲明該屬性為私有,不能在類的外部被使用或直接訪問。在類內部的方法中使用時?self.__private_attrs。
類的私有方法: __private_method:兩個下劃線開頭,聲明該方法為私有方法,不能在類地外部調用。在類的內部調用?self.__private_methods class JustCounter:__secretCount = 0 # 私有變量publicCount = 0 # 公開變量def count(self):self.__secretCount += 1self.publicCount += 1print self.__secretCountcounter = JustCounter() counter.count() counter.count() print counter.publicCount print counter.__secretCount # 報錯,實例不能訪問私有變量 輸出結果如下:1 2 2 Traceback (most recent call last):File "test.py", line 17, in <module>print counter.__secretCount # 報錯,實例不能訪問私有變量 AttributeError: JustCounter instance has no attribute '__secretCount'單下劃線,雙下劃線,頭尾雙下劃線說明:-
__foo__: 定義的是特列方法,類似?__init__()?之類的。
-
_foo: 以單下劃線開頭的表示的是 protected 類型的變量,即保護類型只能允許其本身與子類進行訪問,不能用于?from module import *
-
__foo: 雙下劃線的表示的是私有類型(private)的變量, 只能是允許這個類本身進行訪問了。
2.正則表達式
re.match只匹配字符串的開始,如果字符串開始不符合正則表達式,則匹配失敗,函數返回None;而re.search匹配整個字符串,直到找到一個匹配。
import reline = "Cats are smarter than dogs";matchObj = re.match( r'dogs', line, re.M|re.I) if matchObj:print "match --> matchObj.group() : ", matchObj.group() else:print "No match!!"matchObj = re.search( r'dogs', line, re.M|re.I) if matchObj:print "search --> matchObj.group() : ", matchObj.group() else:print "No match!!"運行結果如下:
No match!! search --> matchObj.group() : dogs匹配和檢索:Python 的 re 模塊提供了re.sub用于替換字符串中的匹配項。
語法:
re.sub(pattern, repl, string, count=0, flags=0)參數:
- pattern : 正則中的模式字符串。
- repl : 替換的字符串,也可為一個函數。
- string : 要被查找替換的原始字符串。
- count : 模式匹配后替換的最大次數,默認 0 表示替換所有的匹配。
實例
import rephone = "2004-959-559 # 這是一個國外電話號碼"# 刪除字符串中的 Python注釋 num = re.sub(r'#.*$', "", phone) print "電話號碼是: ", num# 刪除非數字(-)的字符串 num = re.sub(r'\D', "", phone) print "電話號碼是 : ", num 以上實例執行結果如下: 電話號碼是: 2004-959-559 電話號碼是 : 2004959559下表列出了正則表達式模式語法中的特殊元素。如果你使用模式的同時提供了可選的標志參數,某些模式元素的含義會改變| ^ | 匹配字符串的開頭 |
| $ | 匹配字符串的末尾。 |
| . | 匹配任意字符,除了換行符,當re.DOTALL標記被指定時,則可以匹配包括換行符的任意字符。 |
| [...] | 用來表示一組字符,單獨列出:[amk] 匹配 'a','m'或'k' |
| [^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
| re* | 匹配0個或多個的表達式。 |
| re+ | 匹配1個或多個的表達式。 |
| re? | 匹配0個或1個由前面的正則表達式定義的片段,非貪婪方式 |
| re{ n} | ? |
| re{ n,} | 精確匹配n個前面表達式。 |
| re{ n, m} | 匹配 n 到 m 次由前面的正則表達式定義的片段,貪婪方式 |
| a| b | 匹配a或b |
| (re) | G匹配括號內的表達式,也表示一個組 |
| (?imx) | 正則表達式包含三種可選標志:i, m, 或 x 。只影響括號中的區域。 |
| (?-imx) | 正則表達式關閉 i, m, 或 x 可選標志。只影響括號中的區域。 |
| (?: re) | 類似 (...), 但是不表示一個組 |
| (?imx: re) | 在括號中使用i, m, 或 x 可選標志 |
| (?-imx: re) | 在括號中不使用i, m, 或 x 可選標志 |
| (?#...) | 注釋. |
| (?= re) | 前向肯定界定符。如果所含正則表達式,以 ... 表示,在當前位置成功匹配時成功,否則失敗。 但一旦所含表達式已經嘗試,匹配引擎根本沒有提高;模式的剩余部分還要嘗試界定符的右邊。 |
| (?! re) | 前向否定界定符。與肯定界定符相反;當所含表達式不能在字符串當前位置匹配時成功 |
| (?> re) | 匹配的獨立模式,省去回溯。 |
| \w | 匹配字母數字及下劃線 |
| \W | 匹配非字母數字及下劃線 |
| \s | 匹配任意空白字符,等價于 [\t\n\r\f]. |
| \S | 匹配任意非空字符 |
| \d | 匹配任意數字,等價于 [0-9]. |
| \D | 匹配任意非數字 |
| \A | 匹配字符串開始 |
| \Z | 匹配字符串結束,如果是存在換行,只匹配到換行前的結束字符串。c |
| \z | 匹配字符串結束 |
| \G | 匹配最后匹配完成的位置。 |
| \b | 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
| \B | 匹配非單詞邊界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
| \n, \t, 等. | 匹配一個換行符。匹配一個制表符。等 |
| \1...\9 | 匹配第n個分組的內容。 |
| \10 | 匹配第n個分組的內容,如果它經匹配。否則指的是八進制字符碼的表達式。 |
python高級之面向對象高級
本節內容
1.成員修飾符
python的類中只有私有成員和公有成員兩種,不像c++中的類有公有成員(public),私有成員(private)和保護成員(protected).并且python中沒有關鍵字去修飾成員,默認python中所有的成員都是公有成員,但是私有成員是以兩個下劃線開頭的名字標示私有成員,私有成員不允許直接訪問,只能通過內部方法去訪問,私有成員也不允許被繼承。
class a: # 說明父類的私有成員無法在子類中繼承def __init__(self):self.ge=123self.__gene=456class b(a):def __init__(self,name):self.name=nameself.__age=18super(b,self).__init__() # 這一行會報錯def show(self):print(self.name)print(self.__age)print(self.ge)print(self.__gene) # 這一行也會報錯 obj=b("xiaoming") print(obj.name) print(obj.ge) # print(obj.__gene) # 這個也會報錯 obj.show()上面就是類里面的私有成員了。
2.特殊成員
1.__init__
__init__方法可以簡單的理解為類的構造方法(實際并不是構造方法,只是在類生成對象之后就會被執行),之前已經在上一篇博客中說明過了。
2.__del__
__del__方法是類中的析構方法,當對象消亡的時候(被解釋器的垃圾回收的時候會執行這個方法)這個方法默認是不需要寫的,不寫的時候,默認是不做任何操作的。因為你不知道對象是在什么時候被垃圾回收掉,所以,除非你確實要在這里面做某些操作,不然不要自定義這個方法。
3.__call__
__call__方法在類的對象被執行的時候(obj()或者 類()())會執行。
4.__int__
__int__方法,在對象被int()包裹的時候會被執行,例如int(obj)如果obj對象沒有、__int__方法,那么就會報錯。在這個方法中返回的值被傳遞到int類型中進行轉換。
5.__str__
__str__方法和int方法一樣,當對象被str(obj)包裹的時候,如果對象中沒有這個方法將會報錯,如果有這個方法,str()將接收這個方法返回的值在轉換成字符串。
6.__add__
__add__方法在兩個對象相加的時候,調用第一個對象的__add__方法,將第二個對象傳遞進來,至于怎么處理以及返回值,那是程序員自定義的,就如下面的例子:
class abc:def __init__(self,age):self.age=agedef __add__(self,obj):return self.age+obj.age a1=abc(18) a2=abc(20) print(a1+a2) #執行結果:387.__dict__
__dict__方法在類里面有,在對象里面也有,這個方法是以字典的形式列出類或對象中的所有成員。就像下面的例子:
class abc:def __init__(self,age):self.age=agedef __add__(self,obj):return self.age+obj.age a1=abc(18) print(abc.__dict__) print(a1.__dict__) #執行結果: {'__add__': <function abc.__add__ at 0x0000020666C9E2F0>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'abc' objects>, '__init__': <function abc.__init__ at 0x0000020666C9E268>, '__doc__': None, '__dict__': <attribute '__dict__' of 'abc' objects>} {'age': 18}8.__getitem__ __setitem__ __delitem__
__getitem__方法匹配 對象[索引] 這種方式,__setitem__匹配 對象[索引]=value 這種方式,__delitem__匹配 del 對象[索引] 這種方式,例子如下:
class Foo:def __init__(self,name,age):self.name=nameself.age=agedef __getitem__(self, item): # 匹配:對象[item]這種形式return item+10def __setitem__(self, key, value): # 匹配:對象[key]=value這種形式print(key,value)def __delitem__(self, key): # 匹配:del 對象[key]這種形式print(key)li=Foo("alex",18) print(li[10]) li[10]=100 del li[10] 執行結果: 20 10 100 109.__getslice__ __setslice__ __delslice__
這三種方式在python2.7中還存在,用來對對象進行切片的,但是在python3之后,將這些特殊方法給去掉了,統一使用上面的方式對對象進行切片,因此在使用__getitem__ __setitem__ 這兩個方法之前要先判斷傳遞進參數的類型是不是slice對象。例子如下:
class Foo:def __init__(self,name,age):self.name=nameself.age=ageself.li=[1,2,3,4,5,6,7]def __getitem__(self, item): # 匹配:對象[item]這種形式if isinstance(item,slice): # 如果是slice對象,返回切片后的結果return self.li[item] # 返回切片結果elif isinstance(item,int): # 如果是整形,說明是索引return item+10def __setitem__(self, key, value): # 匹配:對象[key]=value這種形式print(key,value)def __delitem__(self, key): # 匹配:del 對象[key]這種形式print(key)def __getslice__(self,index1,index2):print(index1,index2)li=Foo("alex",18) print(li[3:5]) #執行結果: [4, 5]10. __iter__
類的對象如果想要變成一個可迭代對象,那么對象中必須要有__iter__方法,并且這個方法返回的是一個迭代器。
for 循環的對象如果是一個可迭代的對象,那么會先執行對象中的__iter__方法,獲取到迭代器,然后再執行迭代器中的__next__方法獲取數據。如果for循環的是一個迭代器,那么直接執行迭代器中的__next__方法。
class Foo:def __init__(self,name,age):self.name=nameself.age=agedef __iter__(self):return iter([1,2,3,4,5]) # 返回的是一個迭代器 li=Foo("alex",18)# 1.如果類中有__iter__方法,他的對象就是可迭代對象 # 2.對象.__iter()的返回值是一個迭代器 # 3.for循環的如果是迭代器,直接執行.next方法 # 4.for循環的如果是可迭代對象,先執行對象.__iter(),獲取迭代器再執行nextfor i in li:print(i) #執行結果: 1 2 3 4 511.isinstance和issubclass
之前講過isinstance可以判斷一個變量是否是某一種數據類型,其實,isinstance不只可以判斷數據類型,也可以判斷對象是否是這個類的對象或者是這個類的子類的對象,代碼如下:
class Foo:def __init__(self,name,age):self.name=nameself.age=age class Son(Foo):pass obj=Son("xiaoming",18) print(isinstance(obj,Foo)) 執行結果:Trueissubclass用來判斷一個類是否是某個類的子類,返回的是一個bool類型數據,代碼如下:
class Foo:def __init__(self,name,age):self.name=nameself.age=age class Son(Foo):pass obj=Son("xiaoming",18) print(issubclass(Son,Foo)) 執行結果:True3.類與對象
__new__和__metaclass__
在python中,一切皆對象,我們定義的類其實。。。也是一個對象,那么,類本身是誰的對象呢?在python2.2之前(或者叫經典類中),所有的類,都是class的對象,但是在新式類中,為了將類型(int,str,float等)和類統一,所以,所有的類都是type類型的對象。當然,這個規則可以被修改,在類中有一個屬性 __metaclass__ 可以指定當前類該由哪個類進行實例化。而創建對象過程中,其實構造器不是__init__方法,而是__new__方法,這個方法會返回一個對象,這才是對象的構造器。下面是一個解釋類實例化對象內部實現過程的代碼段:
4.異常處理
python中使用try except finally組合來實現異常撲捉,不像java中是使用try catch finally......其中,except中的Exception是所有異常的父類,下面是一個異常處理的示例:
try:int("aaa") #可能出現異常的代碼 except IndexError as e: # 捕捉索引異常的子異常,注意,這里的as e在老版本的py中可以寫成,e但是新版本中用as e,",e"未來可能會淘汰print("IndexError:",e) except ValueError as e: # 捕捉value錯誤的子異常print("ValueError:",e) except Exception as e: # 如果上面兩個異常沒有捕獲到,那么使用Exception捕獲,Exception能夠捕獲所有的異常print("Exception:",e) else: # 如果沒有異常發生,執行else中的代碼塊print("true") finally: # 不管是否發生異常,在最后都會執行finally中的代碼,假如try里面的代碼正常執行,先執行else中的代碼,再執行finally中的代碼print("finally") 執行結果: ValueError: invalid literal for int() with base 10: 'aaa' finally那么既然Exception是所有異常的父類,我們可以自已定義Exception的子類,實現自定義異常處理,下面就是實現例子:
class OldBoyError(Exception): # 自定義錯誤類型def __init__(self,message):self.message=messagedef __str__(self): # 打印異常的時候會調用對象里面的__str__方法返回一個字符串return self.message try:raise OldBoyError("我錯了...") # raise是主動拋出異常,可以調用自定義的異常拋出異常 except OldBoyError as e:print(e) 執行結果:我錯了...異常處理里面還有一個斷言,一般用在判斷執行環境上面,只要斷言后面的條件不滿足,那么就拋出異常,并且后面的代碼不執行了。
print(123) assert 1==2 # 斷言,故意拋出異常,做環境監測用,環境監測不通過,報錯并結束程序 print("456") 執行結果:assert 1==2 # 斷言,故意拋出異常,做環境監測用,環境監測不通過,報錯并結束程序 123 AssertionError5.反射/自省
python中的反射/自省的實現,是通過hasattr、getattr、setattr、delattr四個內置函數實現的,其實這四個內置函數不只可以用在類和對象中,也可以用在模塊等其他地方,只是在類和對象中用的很多,所以單獨提出來進行解釋。
注意,上面的key都是字符串,而不是變量,也就是說可以通過字符串處理類中的成員或者對象中的屬性。下面是一個例子代碼:
class Foo:def __init__(self,name,age):self.name=nameself.age=agedef show(self):return self.name,self.age obj=Foo("xiaoming",18) print(getattr(obj,"name")) setattr(obj,"k1","v1") print(obj.k1) print(hasattr(obj,"k1")) delattr(obj,"k1") show_fun=getattr(obj,"show") print(show_fun()) 執行結果: xiaoming v1 True ('xiaoming', 18)反射/自省能夠直接訪問以及修改運行中的類和對象的成員和屬性,這是一個很強大的功能,并且并不像java中效率很低,所以用的很多。
下面是一個反射/自省用在模塊級別的例子:
import s2 operation=input("請輸入URL:") if operation in s2.__dict__:getattr(s2,operation)() else:print("404")#模塊s2中的代碼: def f1():print("首頁") def f2():print("新聞") def f3():print("精選") 執行結果: 請輸入URL:f1 首頁6.單例模式
這里介紹一個設計模式,設計模式在程序員寫了兩三年代碼的時候,到一定境界了,才會考慮到設計模式對于程序帶來的好處,從而使用各種設計模式,這里只是簡單的介紹一個簡單的設計模式:單例模式。在面向對象中的單例模式就是一個類只有一個對象,所有的操作都通過這個對象來完成,這就是面向對象中的單例模式,下面是實現代碼:
class Foo: # 單例模式__v=None@classmethoddef ge_instance(cls):if cls.__v:return cls.__velse:cls.__v=Foo()return cls.__v obj1=Foo.ge_instance() print(obj1) obj2=Foo.ge_instance() print(obj2) obj3=Foo.ge_instance() print(obj3) 執行結果: <__main__.Foo object at 0x000001D2ABA01860> <__main__.Foo object at 0x000001D2ABA01860> <__main__.Foo object at 0x000001D2ABA01860>可以看到,三個對象的內存地址都是一樣的,其實,這三個變量中存儲的都是同一個對象的內存地址,這樣有什么好處呢?能夠節省資源,就比如在數據庫連接池的時候就可以使用單例模式,只創建一個類的對象供其他程序調用,還有在web服務中接收請求也可以使用單例模式來實現,節省資源。
總結
以上是生活随笔為你收集整理的python进阶到高阶大全(强烈推荐)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gitlab 如何关闭force pus
- 下一篇: python分布式定时任务_分布式定时任