Python自動化 【第七篇】:Python基礎-面向對象高級語法、異常處理、Scoket開發基礎
本節內容:
1. 面向對象高級語法部分
1.1 靜態方法、類方法、屬性方法
1.2 類的特殊方法
1.3 反射
2. 異常處理
3. Socket開發基礎
1. 面向對象高級語法部分
1.1 靜態方法、類方法、屬性方法
1) 靜態方法
通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。普通的方法,可以在實例化后直接調用,并且在方法里可以通過self.調用實例變量或類變量,但靜態方法是不可以訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實相當于跟類本身已經沒什么關系了,它與類唯一的關聯就是需要通過類名來調用這個方法。


1 class Dog(object): 2 3 def __init__(self, name): 4 5 self.name = name 6 7 8 9 @staticmethod # 把eat方法變為靜態方法 10 11 def eat(self): 12 13 print("%s is eating" % self.name) 14 15 16 17 18 19 d = Dog("taidi") 20 21 d.eat()
上面的調用會出以下錯誤,說是eat需要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法后,再通過實例調用時就不會自動把實例本身當作一個參數傳給self了。


1 <span style="color: #ff0000;">Traceback (most recent call last): 2 3 File "/Users/jieli/PycharmProjects/python基礎/自動化day7面向對象高級/靜態方法.py", line 9, in <module> 4 5 d.eat() 6 7 TypeError: eat() missing 1 required positional argument: 'self' 8 9 </span>
想讓上面的代碼可以正常工作有兩種辦法:
- 調用時主動傳遞實例本身給eat方法,即d.eat(d)
- 在eat方法中去掉self參數,但這也意味著,在eat中不能通過self.調用實例中的其它變量了


1 class Dog(object): 2 3 4 5 def __init__(self,name): 6 7 self.name = name 8 9 10 11 @staticmethod 12 13 def eat(): 14 15 print(" is eating") 16 17 18 19 d = Dog("taidi") 20 21 d.eat() 22
2) 類方法
類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是, 類方法只能訪問類變量,不能訪問實例變量。


1 class Dog(object): 2 3 def __init__(self, name): 4 5 self.name = name 6 7 8 9 @classmethod 10 11 def eat(self): 12 13 print("%s is eating" % self.name) 14 15 16 17 18 19 d = Dog("taidi") 20 21 d.eat()
執行報錯如下,說Dog沒有name屬性,因為name是個實例變量,類方法是不能訪問實例變量的.


1 Traceback (most recent call last): 2 3 File "/Users/jieli/PycharmProjects/python基礎/自動化day7面向對象高級/類方法.py", line 16, in <module> 4 5 d.eat() 6 7 File "/Users/jieli/PycharmProjects/python基礎/自動化day7面向對象高級/類方法.py", line 11, in eat 8 9 print("%s is eating" % self.name) 10 11 AttributeError: type object 'Dog' has no attribute 'name'
此時可以定義一個類變量,也叫name,看下執行效果:


1 class Dog(object): 2 3 name = "我是類變量" 4 5 6 7 def __init__(self, name): 8 9 self.name = name 10 11 12 13 @classmethod 14 15 def eat(self): 16 17 print("%s is eating" % self.name) 18 19 20 21 22 23 d = Dog("taidi") 24 25 d.eat() 26 27 28 29 # 執行結果 30 31 32 33 我是類變量 is eating 34
3) 屬性方法
屬性方法的作用就是通過@property把一個方法變成一個靜態屬性


1 class Dog(object): 2 3 def __init__(self, name): 4 5 self.name = name 6 7 8 9 @property 10 11 def eat(self): 12 13 print(" %s is eating" % self.name) 14 15 16 17 18 19 d = Dog("taidi") 20 21 d.eat()
調用會出以下錯誤,說NoneType is not callable, 因為eat 此時已經變成一個靜態屬性,不是方法了,想調用已經不需要加()號了,直接d.eat就可以.


d = Dog("taidi") d.eat
輸出


taidi is eating
屬性方法實例--查詢航班


1 class Flight(object): 2 def __init__(self, name): 3 self.flight_name = name 4 5 def checking_status(self): 6 print("checking flight %s status" % self.flight_name) 7 return 1 8 9 @property 10 def flight_status(self): 11 status = self.checking_status() 12 if status == 0: 13 print("航班取消。。。") 14 elif status == 1: 15 print("航班到達") 16 elif status == 2: 17 print("航班延遲") 18 else: 19 print("無法確定航班狀態") 20 21 f = Flight("CA980") 22 f.flight_status
現在我只能查詢航班狀態, 既然這個flight_status已經是個屬性了, 那我能否給它賦值呢?


f = Flight("CA980") f.flight_status f.flight_status = 2
輸出結果說不能更改這個屬性:


1 checking flight CA980 status 2 flight is arrived... 3 Traceback (most recent call last): 4 File "/Users/jieli/PycharmProjects/python基礎/自動化day7面向對象高級/屬性方法.py", line 58, in <module> 5 f.flight_status = 2 6 AttributeError: can't set attribute
通過@proerty.setter裝飾器再裝飾一下就可以改了,此時需要一個新方法對這個flight_status進行更改:


1 class Flight(object): 2 def __init__(self, name): 3 self.flight_name = name 4 5 def checking_status(self): 6 print("checking flight %s status" % self.flight_name) 7 return 1 8 9 @property 10 def flight_status(self): 11 status = self.checking_status() 12 if status == 0: 13 print("航班取消。。。") 14 elif status == 1: 15 print("航班到達") 16 elif status == 2: 17 print("航班延遲") 18 else: 19 print("無法確定航班狀態") 20 @flight_status.setter # 修改屬性方法 21 def flight_status(self, status): 22 status_dic = { 23 0: "取消", 24 1: "到達", 25 2: "延遲", 26 } 27 print("\033[31;1m修改航班狀態為%s\033[;0m" % status_dic.get(status)) 28 @flight_status.deleter #刪除 29 def flight_status(self): 30 print("刪除航班狀態") 31 32 f = Flight("CA980") 33 f.flight_status 34 35 f.flight_status = 2 # 觸發@flight_status.setter 36 del f.flight_status # 觸發@flight_status.deleter
1.2 類的特殊方法
1)__doc__ 表示類的描述信息
View Code
2) __module__ 和 __class__
__module__ 表示當前操作的對象在那個模塊
__class__ 表示當前操作的對象的類是什么
View Code
調用C類:
View Code
3) __init__ 構造方法,通過類創建對象時,自動觸發執行。
略
4)__del__
析構方法,當對象在內存中被釋放時,自動觸發執行。
5) __call__ 對象后面加括號,觸發執行。


1 class Foo: 2 def __init__(self): 3 pass 4 5 def __call__(self, *args, **kwargs): 6 print 7 '__call__' 8 9 10 obj = Foo() # 執行 __init__ 11 obj() # 執行 __call__ 12 13
6) __dict__ 查看類或對象中的所有成員
View Code
7)__str__ 如果一個類中定義了__str__方法,那么在打印 對象 時,默認輸出該方法的返回值。
View Code
8)__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分別表示獲取、設置、刪除數據


1 class Foo(object): 2 def __getitem__(self, key): 3 print('__getitem__', key) 4 5 def __setitem__(self, key, value): 6 print('__setitem__', key, value) 7 8 def __delitem__(self, key): 9 print('__delitem__', key) 10 11 12 obj = Foo() 13 14 result = obj['k1'] # 自動觸發執行 __getitem__ 15 obj['k2'] = 'alex' # 自動觸發執行 __setitem__ 16 del obj['k1'] 17 18
9) __new__ \ __metaclass__


1 obj = Foo() 2 obj["name"] = "Tom" 3 #obj["name"] 4 print(obj.data) 5 6 7 class Foo(object): 8 def __init__(self, name): 9 self.name = name 10 11 12 f = Foo("alex")
上述代碼中,obj 是通過 Foo 類實例化的對象,其實,不僅 obj 是一個對象,Foo類本身也是一個對象,因為在Python中一切事物都是對象。
如果按照一切事物都是對象的理論:obj對象是通過執行Foo類的構造方法創建,那么Foo類對象應該也是通過執行某個類的 構造方法 創建。
print type(f) # 輸出:<class '__main__.Foo'> 表示,obj 對象由Foo類創建
print type(Foo) # 輸出:<type 'type'> 表示,Foo類對象由 type 類創建
所以,f對象是Foo類的一個實例,Foo類對象是 type 類的一個實例,即:Foo類對象 是通過type類的構造方法創建。
那么,創建類就可以有兩種方式:
a) 普通方式


1 class Foo(object): 2 def func(self): 3 print 4 'hello Jack'
b) 特殊方式


1 def func(self): 2 print 3 'hello wupeiqi' 4 5 6 Foo = type('Foo', (object,), {'func': func}) 7 # type第一個參數:類名 8 # type第二個參數:當前類的基類 9 # type第三個參數:類的成員
加上構造方法:


1 def func(self): 2 print("hello %s"%self.name) 3 4 def __init__(self,name,age): 5 self.name = name 6 self.age = age 7 Foo = type('Foo',(object,),{'func':func,'__init__':__init__}) 8 9 f = Foo("jack",22) 10 f.func()
1.3 反射
通過字符串映射或修改程序運行時的狀態、屬性、方法, 有以下4個方法:
1) getattr(object, name, default=None)


1 def getattr(object, name, default=None): # known special case of getattr 2 """ 3 getattr(object, name[, default]) -> value 4 5 Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. 6 When a default argument is given, it is returned when the attribute doesn't 7 exist; without it, an exception is raised in that case. 8 """ 9 pass
2) hasattr(object,name)
判斷object中有沒有一個name字符串對應的方法或屬性
3) setattr(x, y, v)


1 def setattr(x, y, v): # real signature unknown; restored from __doc__ 2 """ 3 Sets the named attribute on the given object to the specified value. 4 5 setattr(x, 'y', v) is equivalent to ``x.y = v''
4) delattr(x, y)


1 def delattr(x, y): # real signature unknown; restored from __doc__ 2 """ 3 Deletes the named attribute from the given object. 4 5 delattr(x, 'y') is equivalent to ``del x.y'' 6 """ 7 8
反射代碼示例


1 class Foo(object): 2 def __init__(self): 3 self.name = 'Jack' 4 5 def func(self): 6 return 'func' 7 8 9 obj = Foo() 10 11 # #### 檢查是否含有成員 #### 12 hasattr(obj, 'name') 13 hasattr(obj, 'func') 14 15 # #### 獲取成員 #### 16 getattr(obj, 'name') 17 getattr(obj, 'func') 18 19 # #### 設置成員 #### 20 setattr(obj, 'age', 18) 21 setattr(obj, 'show', lambda num: num + 1) 22 23 # #### 刪除成員 #### 24 delattr(obj, 'name') 25 delattr(obj, 'func')
2. 異常處理
2.1 異常基礎
在編程過程中為了增加友好性,在程序出現bug時一般不會將錯誤信息顯示給用戶,而是現實一個提示的頁面,通俗來說就是不讓用戶看見大黃頁!!!


1 try: 2 pass 3 except Exception as e: 4 pass
需求:將用戶輸入的兩個數字相加


1 while True: 2 num1 = raw_input('num1:') 3 num2 = raw_input('num2:') 4 try: 5 num1 = int(num1) 6 num2 = int(num2) 7 result = num1 + num2 8 except Exception as e: 9 print '出現異常,信息如下:' 10 print(e)
2.2 異常種類
常用異常:
- AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x
- IOError 輸入/輸出異常;基本上是無法打開文件
- ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
- IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
- IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
- KeyError 試圖訪問字典里不存在的鍵
- KeyboardInterrupt Ctrl+C被按下
- NameError 使用一個還未被賦予對象的變量
- SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了)
- TypeError 傳入對象類型與要求的不符合
- UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由于另有一個同名的全局變量,導致你以為正在訪問它
- ValueError 傳入一個調用者不期望的值,即使值的類型是正確的
更多異常


1 ArithmeticError 2 3 AssertionError 4 5 AttributeError 6 7 BaseException 8 9 BufferError 10 11 BytesWarning 12 13 DeprecationWarning 14 15 EnvironmentError 16 17 EOFError 18 19 Exception 20 21 FloatingPointError 22 23 FutureWarning 24 25 GeneratorExit 26 27 ImportError 28 29 ImportWarning 30 31 IndentationError 32 33 IndexError 34 35 IOError 36 37 KeyboardInterrupt 38 39 KeyError 40 41 LookupError 42 43 MemoryError 44 45 NameError 46 47 NotImplementedError 48 49 OSError 50 51 OverflowError 52 53 PendingDeprecationWarning 54 55 ReferenceError 56 57 RuntimeError 58 59 RuntimeWarning 60 61 StandardError 62 63 StopIteration 64 65 SyntaxError 66 67 SyntaxWarning 68 69 SystemError 70 71 SystemExit 72 73 TabError 74 75 TypeError 76 77 UnboundLocalError 78 79 UnicodeDecodeError 80 81 UnicodeEncodeError 82 83 UnicodeError 84 85 UnicodeTranslateError 86 87 UnicodeWarning 88 89 UserWarning 90 91 ValueError 92 93 Warning 94 95 ZeroDivisionError
實例:IndexError


1 dic = ["Jack", 'Rose'] 2 try: 3 dic[10] 4 except IndexError as e: 5 print(e)
實例:KeyError


1 dic = {'k1':'v1'} 2 try: 3 dic['k20'] 4 except KeyError as e: 5 print(e)
實例:ValueError


1 s1 = 'hello' 2 try: 3 int(s1) 4 except ValueError as e: 5 print(e)
寫程序時需要考慮到try代碼塊中可能出現的任意異常,可以這樣寫:


1 s1 = 'hello' 2 try: 3 int(s1) 4 except IndexError as e: 5 print e 6 except KeyError as e: 7 print e 8 except ValueError as e: 9 print(e)
萬能異常 在python的異常中,有一個萬能異常:Exception,他可以捕獲任意異常,即:


s1 = 'hello' try: int(s1) except Exception as e: print(e)
對于特殊處理或提醒的異常需要先定義,最后定義Exception來確保程序正常運行。


1 s1 = 'hello' 2 try: 3 int(s1) 4 except KeyError as e: 5 print('鍵錯誤') 6 except IndexError as e: 7 print('索引錯誤') 8 except Exception as e: 9 print('錯誤’)
2.3 異常其他結構


1 try: 2 # 主代碼塊 3 pass 4 except KeyError as e: 5 # 異常時,執行該塊 6 pass 7 else: 8 # 主代碼塊執行完,執行該塊 9 pass 10 finally: 11 # 無論異常與否,最終執行該塊 12 pass
2.4 主動觸發異常


try: raise Exception('錯誤了。。。') except Exception as e: print(e)
2.5 自定義異常


1 class MyException(Exception): 2 def __init__(self, msg): 3 self.message = msg 4 5 def __str__(self): 6 return self.message 7 8 9 try: 10 raise MyException('我的異常') 11 except MyException as e: 12 print(e)
3. Socket開發基礎
socket通常也稱作"套接字",用于描述IP地址和端口,是一個通信鏈的句柄,應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求。
socket起源于Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,對于文件用【打開】【讀寫】【關閉】模式來操作。socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)
socket和file的區別:
- file模塊是針對某個指定文件進行【打開】【讀寫】【關閉】
- socket模塊是針對 服務器端 和 客戶端Socket 進行【打開】【讀寫】【關閉】
socket server 端代碼:


1 import socket 2 3 ip_port = ('127.0.0.1',9999) 4 5 sk = socket.socket() 6 sk.bind(ip_port) 7 sk.listen(5) 8 9 while True: 10 print('server waiting...') 11 conn,addr = sk.accept() 12 13 client_data = conn.recv(1024) 14 print(client_data) 15 conn.sendall('不要回答') 16 17 conn.close()
socket client 端代碼:


1 import socket 2 ip_port = ('127.0.0.1', 9999) 3 4 sk = socket.socket() 5 sk.connect(ip_port) 6 7 sk.sendall('請求占領地球') 8 9 server_reply = sk.recv(1024) 10 print(server_reply) 11 12 sk.close()
web服務應用:


1 import socket 2 3 4 def handle_request(client): 5 buf = client.recv(1024) 6 client.send("HTTP/1.1 200 OK\r\n\r\n") 7 client.send("Hello, World") 8 9 10 def main(): 11 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 12 sock.bind(('localhost', 8080)) 13 sock.listen(5) 14 15 while True: 16 connection, address = sock.accept() 17 handle_request(connection) 18 connection.close() 19 20 21 if __name__ == '__main__': 22 main()
更多功能:
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
參數一:地址簇
socket.AF_INET IPv4(默認)
socket.AF_INET6 IPv6
socket.AF_UNIX 只能夠用于單一的Unix系統進程間通信
參數二:類型
socket.SOCK_STREAM 流式socket , for TCP (默認)
socket.SOCK_DGRAM 數據報式socket , for UDP
socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。
socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限于高級用戶或管理員運行的程序使用。
socket.SOCK_SEQPACKET 可靠的連續數據包服務
參數三:協議
0 (默認)與特定的地址家族相關的協議,如果是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議。
- sk.bind(address) 將套接字綁定到地址。address地址的格式取決于地址族。在AF_INET下,以元組(host,port)的形式表示地址。
- sk.listen(backlog) 開始監聽傳入連接。backlog指定在拒絕連接之前,可以掛起的最大連接數量。backlog等于5,表示內核已經接到了連接請求,但服務器還沒有調用accept進行處理的連接個數最大為5。這個值不能無限大,因為要在內核中維護連接隊列。
- sk.setblocking(bool) 是否阻塞(默認True),如果設置False,那么accept和recv時一旦無數據,則報錯。
- sk.accept() 接受連接并返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。接收TCP 客戶的連接(阻塞式)等待連接的到來。
- sk.connect(address) 連接到address處的套接字。一般,address的格式為元組(hostname,port),如果連接出錯,返回socket.error錯誤。
- sk.connect_ex(address) 同上,只不過會有返回值,連接成功時返回 0 ,連接失敗時候返回編碼,例如:10061
- sk.close() 關閉套接字
- sk.recv(bufsize[,flag]) 接受套接字的數據。數據以字符串形式返回,bufsize指定最多可以接收的數量。flag提供有關消息的其他信息,通常可以忽略。
- sk.recvfrom(bufsize[.flag]) 與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
- sk.send(string[,flag]) 將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小于string的字節大小。即:可能未將指定內容全部發送。
- sk.sendall(string[,flag]) 將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。內部通過遞歸調用send,將所有內容發送出去。
- sk.sendto(string[,flag],address) 將數據發送到套接字,address是形式為(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用于UDP協議。
- sk.settimeout(timeout) 設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值為None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因為它們可能用于連接的操作(如 client 連接最多等待5s )
- sk.getpeername() 返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。
- sk.getsockname() 返回套接字自己的地址。通常是一個元組(ipaddr,port)
- sk.fileno() 套接字的文件描述符
文章列表