Python學習-08-面向對象編程進階和異常處理

1、類的進階html

1)靜態方法:python

跟類沒有關係了。是一個單純的函數。調用的時候,必須用類名來調用。(臺灣與大陸的關係。)至關於類下面的一個函數,跟類沒有關係,不能直接調用類的參數。程序員

靜態方法:只是名義上歸類管理,實際上靜態方法訪問不了類或實例中的任何屬性。 數據庫

經過@staticmethod裝飾器便可把其裝飾的方法變爲一個靜態方法,什麼是靜態方法呢?其實不難理解,普通的方法,能夠在實例化後直接調用,而且在方法裏能夠經過self.調用實例變量或類變量,但靜態方法是不能夠訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實至關於跟類自己已經沒什麼關係了,它與類惟一的關聯就是須要經過類名來調用這個方法windows

 1 class Dog(object):
 2 
 3     def __init__(self,name):
 4 
 5         self.name = name
 6 
 7     @staticmethod #把eat方法變爲靜態方法
 8 
 9     def eat(self):
10 
11         print("%s is eating" % self.name)
12 
13 d = Dog("張三")
14 
15 d.eat()

上面的調用會出如下錯誤,說是eat須要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法後,再經過實例調用時就不會自動把實例自己看成一個參數傳給self了。框架

TypeError: eat() missing 1 required positional argument: 'self'ide

想讓上面的代碼能夠正常工做有兩種辦法函數

1. 調用時主動傳遞實例自己給eat方法,即d.eat(d)測試

2. 在eat方法中去掉self參數,但這也意味着,在eat中不能經過self.調用實例中的其它變量了ui

 1 class Dog(object):
 2 
 3     def __init__(self,name):
 4 
 5         self.name = name
 6 
 7     @staticmethod
 8 
 9     def eat():
10 
11         print(" is eating")
12 
13 d = Dog("張三")
14 
15 d.eat()

2)類方法:只能訪問類變量,不是訪問實例變量。用途無。@classmethod

 1 class Dog(object):
 2     def __init__(self,name):
 3         self.name = name
 4  
 5     @classmethod
 6     def eat(self):
 7         print("%s is eating" % self.name)
 8  
 9  
10  
11 d = Dog("張三")
12 d.eat()

執行報錯以下,說Dog沒有name屬性,由於name是個實例變量,類方法是不能訪問實例變量的

AttributeError: type object 'Dog' has no attribute 'name'

 1 class Dog(object):
 2     name = "我是類變量"
 3     def __init__(self,name):
 4         self.name = name
 5  
 6     @classmethod
 7     def eat(self):
 8         print("%s is eating" % self.name)
 9 
10 d = Dog("張三")
11 d.eat()
12   
13 #執行結果
14  
15 我是類變量 is eating
 

3)屬性方法:@property。把一個方法變成靜態屬性。就是不能傳參數。(重點)

舉例:一個方法變成屬性,如何修改它,如何刪除它

 1 class Dog(object):
 2     '''這個類是描述狗這個對象的'''
 3     def __init__(self,name):
 4         self.name = name
 5         self.__food = None
 6     @property #attribute屬性方法,把方法變成了屬性,沒法調用參數
 7     def eat(self):
 8         print("%s is eating %s" %(self.name,self.__food))
 9     @eat.setter#修改屬性方法,經過self.__food,修改屬性方法
10     def eat(self,food):
11         print("set to food:",food)
12         self.__food = food
13     @eat.deleter#輸出屬性方法
14     def eat(self):
15         del self.__food
16         print("刪完了")
17     def talk(self):
18         print("%s is talking"% self.name)
19 d = Dog("張三")
20 d.eat
21 d.eat=("包子")
22 d.eat
23 del d.eat

屬性方法的應用:

既然想要靜態變量,那直接定義成一個靜態變量不就得了麼?well, 之後你會需到不少場景是不能簡單經過定義靜態屬性來實現的,好比,你想知道一個航班當前的狀態,是到達了、延遲了、取消了、仍是已經飛走了(第三方公司),想知道這種狀態你必須經歷如下幾步:

1. 鏈接航空公司API查詢

2. 對查詢結果進行解析

3. 返回結果給你的用戶

所以這個status屬性的值是一系列動做後才獲得的結果,因此你每次調用時,其實它都要通過一系列的動做才返回你結果,但這些動做過程不須要用戶關心,用戶只須要調用這個屬性就能夠,明白了麼?

 1 class Flight(object):                    
 2 
 3     def __init__(self,name):
 4 
 5         self.flight_name = name
 6 
 7     def checking_status(self):
 8 
 9         print("checking flight %s status " % self.flight_name)
10 
11         return  1
12 
13     @property
14 
15     def flight_status(self):
16 
17         status = self.checking_status()
18 
19         if status == 0 :
20 
21             print("flight got canceled...")
22 
23         elif status == 1 :
24 
25             print("flight is arrived...")
26 
27         elif status == 2:
28 
29             print("flight has departured already...")
30 
31         else:
32 
33             print("cannot confirm the flight status...,please check later")
34 
35 f = Flight("CA980")
36 
37 f.flight_status

cool , 那如今我只能查詢航班狀態,既然這個flight_status已是個屬性了,那我可否給它賦值呢?試試吧

1 f = Flight("CA980")
2 f.flight_status
3 f.flight_status =  2

輸出, 說不能更改這個屬性,我擦。。。。,怎麼辦怎麼辦。。。

checking flight CA980 status

flight is arrived... 

    f.flight_status =  2

AttributeError: can't set attribute

固然能夠改,不過須要經過@proert

y.setter裝飾器再裝飾一下,此時 你須要寫一個新方法, 對這個flight_status進行更改。

 1  class Flight(object):
 2     def __init__(self,name):
 3         self.flight_name = name
 4     def checking_status(self):
 5         print("checking flight %s status " % self.flight_name)
 6         return  1
 7     @property
 8     def flight_status(self):
 9         status = self.checking_status()
10         if status == 0 :
11             print("flight got canceled...")
12         elif status == 1 :
13             print("flight is arrived...")
14         elif status == 2:
15             print("flight has departured already...")
16         else:
17             print("cannot confirm the flight status...,please check later")  
18 
19     @flight_status.setter #修改
20     def flight_status(self,status):
21         status_dic = {
22 : "canceled",
23 :"arrived",
24 : "departured"
25         }
26         print("\033[31;1mHas changed the flight status to \033[0m",status_dic.get(status) )
27     @flight_status.deleter  #刪除
28     def flight_status(self):
29         print("status got removed...")
30 f = Flight("CA980")
31 f.flight_status
32 f.flight_status =  2 #觸發@flight_status.setter
33 del f.flight_status #觸發@flight_status.deleter

注意以上代碼裏還寫了一個@flight_status.deleter, 是容許能夠將這個屬性刪除。

 4)類的特殊成員方法:

1. __doc__  表示類的描述信息

1 class Foo:
2 
3     """ 描述類信息,這是用於看片的神奇 """
4 
5     def func(self):
6 
7         pass
8 
9 print Foo.__doc__#輸出:類的描述信息

2.  __init__ 構造方法,經過類建立對象時,自動觸發執行。

3. __del__ 析構方法,當對象在內存中被釋放時,自動觸發執行。

注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的

4. __call__ 對象後面加括號,觸發執行。

注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

5. __dict__ 查看類或對象中的全部成員  

6.__str__ 若是一個類中定義了__str__方法,那麼在打印 對象 時,默認輸出該方法的返回值。

 1 class Dog(object):
 2     '''這個類是描述狗這個對象的'''
 3     def __init__(self,name):
 4         self.name = name
 5     def eat(self,food):
 6         print("%s is eating %s" %(self.name,food))
 7     def talk(self):
 8         print("%s is talking"% self.name)
 9     def __call__(self, *args, **kwargs):
10         print("running call",args,kwargs)
11     def __str__(self):#打印對象的時候,輸入該方法的返回值。默認是內存地址
12         return "<obj:%s>" %self.name
13 
14 #print(Dog.__doc__) #打印描述信息
15 #測試__call__方法
16 d=Dog("張三")
17 #d(1,2,3,name=123)
18 #上面的能夠換爲:
19 #Dog("張三")()
20 #print(Dog.__dict__) #打印類裏的全部屬性,不包括實例屬性
21 #print(d.__dict__) #打印全部實例屬性,不包括類屬性

7. __getitem__、__setitem__、__delitem__

用於索引操做,如字典。以上分別表示獲取、設置、刪除數據

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

8.__new__    、 __metaclass__ (底層框架要用) 

 1 class Foo(object):
 2 
 3      def __init__(self, name):
 4 
 5          self.name = name
 6 
 7 f = Foo("alex")
 8 
 9 print(type(f))
10 
11 print(type(Foo))

上述代碼中,f是經過 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 
3     def func(self):
4 
5         print 'hello alex'

b). 特殊方式

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

a)      和b) 兩個都是同樣的,都是實現建立類的。

舉例代碼

 1 def func(self):
 2 
 3     print("hello %s"%self.name)
 4 
 5 def __init__(self,name,age):
 6 
 7     self.name = name
 8 
 9     self.age = age
10 
11 Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
12 
13 f = Foo("jack",22)
14 
15 f.func()

So ,記住,類是由 type 類實例化產生

那麼問題來了,類默認是由 type 類實例化產生,type類中如何實現的建立類?類又是如何建立對象?

答:類中有一個屬性 __metaclass__,其用來表示該類由誰來實例化建立,因此,咱們能夠爲 __metaclass__ 設置一個type類的派生類,從而查看類建立的過程。 

參考代碼;

 1 class MyType(type):
 2 
 3     def __init__(self, what, bases=None, dict=None):
 4 
 5         print("--MyType init---")
 6 
 7         super(MyType, self).__init__(what, bases, dict)
 8 
 9     def __call__(self, *args, **kwargs):
10 
11         print("--MyType call---")
12 
13         obj = self.__new__(self, *args, **kwargs)
14 
15         obj.data = {"name":111}
16 
17         self.__init__(obj, *args, **kwargs)
18 
19 class Foo(object):
20 
21     __metaclass__ = MyType
22 
23     def __init__(self, name):
24 
25         self.name = name
26 
27         print("Foo ---init__")
28 
29     def __new__(cls, *args, **kwargs):
30 
31         print("Foo --new--")
32 
33         #print(object.__new__(cls))
34 
35         return object.__new__(cls) #繼承父親的__new__方法
36 
37 # 第一階段:解釋器從上到下執行代碼建立Foo類
38 
39 # 第二階段:經過Foo類建立obj對象
40 
41 obj = Foo("Alex")
42 
43 print(obj.name)
View Code

new是用來建立實例的。

類的生成 調用 順序依次是 __new__ --> __init__ --> __call__

metaclass 詳解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那個答案寫的很是好

 九、反射:經過字符串映射或修改程序運行時的狀態、屬性、方法, 有如下4個方法:

hasattr():第一個是實例化名字,第二個是字符串形式  反射 判斷一個對象裏是否有字符串對應的方法

getattr():若是是方法,獲取並調用,根據字符串獲取obj對象裏的對應的方法的內存地址。

            若是是動態屬性,則直接返回屬性的值

setattr():經過字符串設置屬性。setattr(obj,’y’,’z’)  等價於x.y=z

delattr():刪除屬性或者方法

 1 def bulk(self):
 2     print("%s is yelling...." %self.name)
 3 
 4 class Dog(object):
 5     def __init__(self,name):
 6         self.name = name
 7 
 8     def eat(self,food):
 9         print("%s is eating..."%self.name,food)
10 
11 d = Dog("李四")
12 choice = input(">>:").strip()
13 
14 if hasattr(d,choice):#第一個是實例化名字,第二個是字符串形式  反射 判斷一個對象裏是否有字符串對應的方法
15 
16    getattr(d,choice)#若是是方法,獲取並調用,根據字符串獲取obj對象裏的對應的方法的內存地址。
17                     #若是是動態屬性,則直接返回屬性的值
18 Delattr(d,choice)#刪除屬性或者方法
19 
20 else:
21 setattr(d,choice,bulk) #d.talk = bulk 經過字符串設置屬性。
22 #setattr(obj,’y’,’z’)  等價於x.y=z。上面的choice是talk,d.talk=bulk
23 #d.talk(d)
24 
25     func = getattr(d, choice)
26 
27     func(d) 

再次舉例:

 1 class Foo(object):
 2 
 3     def __init__(self):
 4 
 5         self.name = 'wupeiqi'
 6 
 7      def func(self):
 8 
 9         return 'func'
10 
11  obj = Foo()
12 
13  
14 
15 # #### 檢查是否含有成員 ####
16 
17 hasattr(obj, 'name')
18 
19 hasattr(obj, 'func')
20 
21  
22 
23 # #### 獲取成員 ####
24 
25 getattr(obj, 'name')
26 
27 getattr(obj, 'func')
28 
29  
30 
31 # #### 設置成員 ####
32 
33 setattr(obj, 'age', 18)
34 
35 setattr(obj, 'show', lambda num: num + 1)
36 
37  
38 
39 # #### 刪除成員 ####
40 
41 delattr(obj, 'name')
42 
43 delattr(obj, 'func')
View Code

異常處理:

經過提早預判錯誤,打印提示

1 data={}
2 
3 try:
4 
5     data['name']
6 
7 except KeyError as e:
8 
9     print("沒有這個Key",e)

寫程序時須要考慮到try代碼塊中可能出現的任意異常,能夠這樣寫:多個預判錯誤:

在Python執行時,當遇到第一個錯誤就會中止。

 1 data={}
 2 
 3 name=[]
 4 
 5 try:
 6 
 7     name[2]
 8 
 9     data['name']
10 
11 except KeyError as e:
12 
13     print("沒有這個Key",e)
14 
15 except IndexError as e:
16 
17     print("列表操做錯誤",e)

還能夠這樣寫:應用於不管什麼錯誤類型,處理結果同樣的狀況。

 1 data={}
 2 
 3 name=[]
 4 
 5 try:
 6 
 7     name[2]
 8 
 9     data['name']
10 
11 except (KeyError,IndexError) as e:
12 
13     print("沒有這個Key",e)

萬能異常 在python的異常中,有一個萬能異常:Exception,他能夠捕獲任意異常,即(萬能錯誤類型)

 1 data={}
 2 
 3 name=[]
 4 
 5 try:
 6 
 7     name[2]
 8 
 9     data['name']
10 
11 except Exception as e: # Exception 任意錯誤,通常不用不容易調試判斷
12 
13     print("沒有這個Key",e)

windows中的未知錯誤?怎麼作到的呢?咱們用Python試一試?

 1 data={}
 2 
 3 name=[]
 4 
 5 try:
 6 
 7     open("a.txt")
 8 
 9 except KeyError as e:
10 
11     print("沒有這個Key",e)
12 
13 except IndexError as e:
14 
15     print("列表操做錯誤",e)
16 
17 except Exception as e:
18 
19 print("未知錯誤",e)

else 和 finally 用法:

 1 data={}
 2 
 3 name=[]
 4 
 5 try:
 6 
 7    #open("a.txt")
 8 
 9    a=1
10 
11 except KeyError as e:
12 
13     print("沒有這個Key",e)
14 
15 except IndexError as e:
16 
17     print("列表操做錯誤",e)
18 
19 except Exception as e:
20 
21     print("未知錯誤",e)
22 
23 else:
24 
25     print("一切正常")
26 
27 finally:
28 
29     print("不管有沒有錯,都會執行")

注意:

1)Python3中的as在Python2.7中用的是‘,’。

2)一些抓不到的錯誤,好比說縮進、語法致使的錯誤。IndentationError: unexpected indent

經常使用的錯誤類型:

 1 AttributeError 試圖訪問一個對象沒有的樹形,好比foo.x,可是foo沒有屬性x
 2 IOError 輸入/輸出異常;基本上是沒法打開文件
 3 ImportError 沒法引入模塊或包;基本上是路徑問題或名稱錯誤
 4 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
 5 IndexError 下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5]
 6 KeyError 試圖訪問字典裏不存在的鍵
 7 KeyboardInterrupt Ctrl+C被按下
 8 NameError 使用一個還未被賦予對象的變量
 9 SyntaxError Python代碼非法,代碼不能編譯
10 TypeError 傳入對象類型與要求的不符合
11 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是因爲另有一個同名的全局變量,致使你覺得正在訪問它
12 ValueError 傳入一個調用者不指望的值,即便值的類型是正確的
View Code

更多的錯誤

 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
View Code

主動觸發異常:

1 try:
2 
3     raise Exception('錯誤了。。。')
4 
5 except Exception,e:
6 
7     print e

自定義異常:注意不要覆蓋已經存在的異常。

 1 class AlexError(Exception):
 2 
 3     def __init__(self, msg):
 4 
 5         self.message = msg
 6 
 7 try:
 8 
 9     raise AlexError('數據庫連不上')
10 
11 except AlexError as e:
12 
13     print(e)

 參考文獻:

http://www.cnblogs.com/alex3714/articles/5213184.html

 http://www.cnblogs.com/wupeiqi/articles/5017742.html   

相關文章
相關標籤/搜索