面向對象的高級用法shell
1. __str__編程
只要執行打印對象的操做,就會觸發該對象類中的__str__方法(也就是對象的綁定方法)
它是一種默認的方法,默認的打印輸出爲<__main__.Foo object at 0x003EE350>,可是若是將該綁定方法
在類中重寫的話,要求必須有以字符串類型的返回值,返回形式能夠本身設定。json
class Foo: def __init__(self,name,age): self.name = name self.age = age def __str__(self): return 'name:%s age:%d' % (self.name,self.age)#返回值必須有 obj=Foo('Albert',18) print(obj)
2. __del__(析構函數)windows
由類產生的對象是存放在內存中的,程序結束後要釋放掉對象,則會觸發__del__方法執行設計模式
class Foo: def __init__(self,name,age): self.name = name self.age = age def __del__(self): #注意:必須是程序執行結束後纔會執行該方法 print('__del__') obj=Foo('Albert',18) del obj #此時是主動觸發執行__del__方法 print('after __del__')
3. __setitem__, __getitem__, __delitem__緩存
利用字典的方式來操做對象的屬性網絡
#方法修改以前: class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): print('getitem') def __setitem__(self, key, value): print('setitem') def __delitem__(self, key): print('delitem') obj = Foo('egon') print(obj.__dict__) #{'name': 'egon'} obj['name'] = 'Albert' #雖然能夠調用__setitem__方法,可是沒法完成真正的修改操做 print(obj.__dict__) #{'name': 'egon'} obj.__dict__['name'] = 'Albert' #正確的修改方式 print(obj.__dict__) #{'name': 'Albert'} #方法修改以後: class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): print('getitem') return self.__dict__[item] def __setitem__(self, key, value): print('setitem') self.__dict__[key] = value def __delitem__(self, key): print('delitem') self.__dict__.pop(key) obj = Foo('egon') obj['name'] = 'Albert' #調用__setitem__ print(obj['name']) #調用__getitem__ del obj['name'] #調用__delitem__ print(obj.__dict__) #結果:{},空字典,屬性被刪除
4. __getattr__,__setattr__,__delattr__app
class Foo: def __init__(self,x): self.x = x def __getattr__(self, item): print('getattr') def __setattr__(self, key, value): print('setattr') self.__dict__[key] = value def __delattr__(self, item): print('delattr') self.__dict__.pop(item) # obj=Foo() # obj.x = 1 #觸發__setattr__,但未執行成功 # print(obj.__dict__) # del obj.x #觸發__delattr__,但未執行成功 # print(obj.__dict__) # print(obj.x) #觸發__getattr__ obj = Foo(10) #觸發__setattr__ print(obj.x) #沒有觸發__getattr__ print(obj.__dict__) print(obj.y) #當屬性不存在的時候纔會觸發__getattr__ del obj.x #觸發__delattr__ print(obj.x) #觸發__getattr__,說明x已經被刪除
二次加工標準類型socket
1. 繼承函數
須要改寫的類型是一個類,能夠經過繼承的方式實現
需求:改寫list規定只能加入字符串類型數據 class List(list): class List(list): def __init__(self,item_list,tag=False): super().__init__(item_list) self.tag = tag def append(self, p_object): if not isinstance(p_object,str): #判斷要加入的元素是不是字符串,非字符串元素會報錯 raise TypeError('must be str') else: super().append(p_object) #繼承父類的方法 @property def mid_num(self): mid_index = len(self) // 2 return self[mid_index] def clear(self): if not self.tag: raise PermissionError('not allowed')#查看是否有清除列表權限 super().clear() #繼承父類的方法 self.tag = False l = List([1,2,3]) l.append('a') print(l) print(l.mid_num) l.tag = True l.clear() print(l)
2. 受權
針對你須要改寫的類型它不是一個類,沒法用繼承的方式實現,只能用受權的方式實現
import time class Open: def __init__(self,filepath,mode='r',encoding='utf-8'): self.filepath = filepath self.mode = mode self.encoding = encoding self.ff = open(self.filepath,mode=self.mode,encoding=self.encoding) def write(self,msg): t = time.strftime('%Y-%m-%d %X') self.ff.write('%s %s' % (t,msg)) def __getattr__(self, item): return getattr(self.ff, item) obj = Open('a.txt','w',encoding='utf-8') #未重寫write方法時調用方式 # obj.ff.write('111\n') # obj.ff.write('222\n') # obj.ff.write('333\n') # obj.ff.close() #重寫write方法後的調用方式 obj.write('aaa\n') obj.write('bbb\n') obj.write('ccc\n') print(obj.seek) #<built-in method seek of _io.TextIOWrapper object at 0x0056A530> obj.close()
3. __next__和__iter__實現迭代器協議
class Foo: def __init__(self,n_start,n_stop): self.n_start = n_start self.n_stop = n_stop def __next__(self): if self.n_start >= self.n_stop: raise StopIteration #遍歷到最後報出異常
x = self.n_start self.n_start += 1 return x def __iter__(self): return self obj = Foo(0,10) # print(next(obj)) # print(next(obj)) # print(next(obj)) for i in obj: print(i) #至關於 for i in range(10): print(i)
4. __enter__和__exit__實現上下文管理協議
class Open: def __init__(self,name,mode='w',encoding='utf-8'): self.name = name self.mode = mode self.encoding = encoding self.f = open(self.name,mode=self.mode,encoding=self.encoding) def __enter__(self): print('__enter__') return self.f def __exit__(self, exc_type, exc_val, exc_tb): #整個代碼塊結束觸發 print('__exit__') # print('exc_type',exc_type) #異常類型 # print('exc_val',exc_val) #異常的值 # print('exc_tb',exc_tb) #異常的追蹤信息 self.f.close() # return True #處理異常,保證異常所處子代碼塊之外的代碼正常進行 obj = Open('b.txt','w')#沒有文件的話會自動建立 print(obj) #<__main__.Open object at 0x002559F0> with Open('a.txt') as f: #with Open('a.txt')操做的結果就是觸發__enter__返回self.f, 以後as f至關於f=self.f print(f) #<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'> # 1/0 f.write('333\n')
5. __call__方法
class Foo: def __call__(self, *args, **kwargs): print('===>') obj=Foo() obj() #若是類內部沒有定義__call__方法,對象是不能以加括號的方式調用的。
網絡編程
1. socket是什麼?
socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,socket其實就是一個門面模式,
它把複雜的TCP/IP協議族隱藏在socket接口後面,對用戶來講,一組簡單的接口就是所有,讓socket去組織數據,以符合
指定的協議。咱們無需深刻理解TCP/UDP協議,socket已經爲咱們封裝好了,咱們只須要遵循socket的規定去編程,寫出
的程序天然就是遵循TCP/UDP標準的。
2. 基於TCP協議的socket的簡單實現
Server端實現 import socket #socket.AF_INET 指定套接字地址家族 #socket.SOCK_STREAM 指TCP流式協議 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind(('127.0.0.1',8080)) #綁定IP地址端口號 phone.listen(5) #bind鏈接池 #conn爲三次握手成功後創建的鏈接 #addr爲客戶端的地址 conn,addr = phone.accept() #等待鏈接 print('conn',conn) print('client addr',addr) client_msg = conn.recv(1024) #收消息 print('clent msg: %s' % client_msg) conn.send(client_msg.upper()) #發送消息 conn.close() #關閉鏈接 phone.close() #關閉通訊 Client端實現 import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1', 8080)) #客戶端發起鏈接 phone.send('Hello'.encode('utf-8')) #發消息 back_msg = phone.recv(1024) print(back_msg) phone.close()
3. 通訊循環和鏈接循環
Server端實現 import socket #socket.AF_INET 指定套接字地址家族 #socket.SOCK_STREAM 指TCP流式協議 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) #綁定IP地址端口號 phone.listen(5) #bind鏈接池 while True:#鏈接循環 # conn爲三次握手成功後創建的鏈接 # addr爲客戶端的地址 conn, addr = phone.accept() # 等待鏈接 print('conn', conn) print('client addr', addr) while True: # 與conn的通訊循環 try: client_msg = conn.recv(1024) # 收消息 if not client_msg: break #針對Linux平臺,收空內容後斷開客戶端鏈接,windows平臺下可不寫 print('clent msg: %s' % client_msg) conn.send(client_msg.upper()) # 發送消息 except Exception: # 解決服務端的異常終止 break conn.close() # 關閉鏈接 phone.close() #關閉通訊 Client端實現 import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1', 8080)) #客戶端發起鏈接 while True:#通訊循環 msg = input('>>>:').strip() if not msg: continue #解決客服端發送空內容的問題 phone.send(msg.encode('utf-8')) #發消息 back_msg = phone.recv(1024) print(back_msg) phone.close()
4. 基於socket實現遠程執行shell命令
Server端實現 import socket import subprocess #socket.AF_INET 指定套接字地址家族 #socket.SOCK_STREAM 指TCP流式協議 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) #綁定IP地址端口號 phone.listen(5) #bind鏈接池 while True:#鏈接循環 # conn爲三次握手成功後創建的鏈接 # addr爲客戶端的地址 conn, addr = phone.accept() # 等待鏈接 print('conn', conn) print('client addr', addr) while True: # 與conn的通訊循環 try: cmd = conn.recv(1024) # 收消息 if not cmd: break res = subprocess.Popen(cmd.decode('utf-8'),shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() conn.send(cmd_res) except Exception: # 解決服務端的異常終止 break conn.close() # 關閉鏈接 phone.close() #關閉通訊 Client端實現 import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1', 8080)) #客戶端發起鏈接 while True:#通訊循環 cmd = input('>>>:').strip() if not cmd: continue #解決客服端發送空內容的問題 phone.send(cmd.encode('utf-8')) #發消息 cmd_res = phone.recv(1024) print(cmd_res.decode('gbk')) phone.close()
自定義包頭解決粘包問題
P.S.只有TCP有粘包現象,UDP永遠不會粘包
什麼是粘包?
所謂粘包問題主要仍是由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的。這取決於TCP的工做原理。
粘包發生在客戶端:受限於網絡傳送介質的速度,將來得及每條都及時發送給服務端,致使發送的數據在客戶端的緩存堆積並一塊送到服務端
粘包發生在服務端:服務端的接收的數據在緩存中未及時被取完,致使接下來從客戶端發送過來的數據堆積在服務端的緩存,下一次可能被一併取出
CPU工做的兩種狀態
內核態:運行操做系統,能夠操做硬件
用戶態:運行用戶的應用程序
如何解決粘包的問題:
須要本身定製報頭讓發送端在發送數據以前,把本身將要發送的字節流總大小讓接收端知曉,而後接收端來一個死循環接收完全部的數據。
Server端實現 import socket import subprocess import struct import json #socket.AF_INET 指定套接字地址家族 #socket.SOCK_STREAM 指TCP流式協議 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8080)) #綁定IP地址端口號 phone.listen(5) #bind鏈接池 while True:#鏈接循環 # conn爲三次握手成功後創建的鏈接 # addr爲客戶端的地址 conn, addr = phone.accept() # 等待鏈接 print('conn=', conn) print('client addr=', addr) while True: # 與conn的通訊循環 try: cmd = conn.recv(1024) # 收消息 if not cmd: break res = subprocess.Popen(cmd.decode('utf-8'),shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() if err: cmd_res = err else: cmd_res = res.stdout.read() # conn.send(struct.pack('i',len(cmd_res))) #先發報頭 head_dict={'filename':None,'hash':None,'total_size':len(cmd_res)} head_json = json.dumps(head_dict) #報頭信息序列化 head_bytes = head_json.encode('utf-8')#將報頭信息轉化爲字節形式傳輸 conn.send(struct.pack('i',len(head_bytes))) #發送報頭長度 conn.send(head_bytes) #再發送報頭數據 conn.send(cmd_res) #再發真實的數據 except Exception: # 解決服務端的異常終止 break conn.close() # 關閉鏈接 phone.close() #關閉通訊 Client端實現 import socket import struct import json phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1', 8080)) #客戶端發起鏈接 while True:#通訊循環 cmd = input('>>>:').strip() if not cmd: continue #解決客服端發送空內容的問題 phone.send(cmd.encode('utf-8')) #發消息 head_len_info = phone.recv(4) #收到報頭的長度信息 head_len = struct.unpack('i', head_len_info)[0] #獲得報頭的長度 head_bytes = phone.recv(head_len) #獲取報頭信息 head_json = head_bytes.decode('utf-8') #報頭信息反序列化 head_dict = json.loads(head_json) #獲取報頭字典格式 total_size = head_dict['total_size'] #從字典中取出真實的數據 # total_size = struct.unpack('i',head)[0] recv_size = 0 data = b'' while recv_size < total_size: recv_data = phone.recv(1024) data += recv_data recv_size += len(recv_data) print(data.decode('gbk')) phone.close()