Python自動化開發課堂筆記【Day08】 - Python進階(面向對象的高級用法,網絡編程)

面向對象的高級用法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()
相關文章
相關標籤/搜索