面向對象高級-day8
內容概要
- 上節回顧
- 對象的內置方法
- 源類
- 異常處理
- 編寫一個FTP程序(面向對象的應用)
一、上節回顧
1.1.靜態方法和類方法(區別)
- 靜態方法和本身並沒有什麽關系,只是在調用的時候需要加上類名
- 類方法可以方法類變量,不可以調用實例變量
- 其實類方法和靜態方法在實際的運用中並不是很多見
class Foo(object): def __init__(self,name): self.name = name @staticmethod def py_print(self): print("this is you‘re %name"%self.name) f = Foo(‘LeonYan‘) f.py_print() #錯誤的使用累的屬性 f.py_print(f) #靜態方法調用類方法需要有幾個將參數傳入進去,換而言之就是靜態方法在調用的時候不需要傳參數
1.2.屬性方法——property(把一個類方法變成一個類屬性)
在調用的時候不用加括號
觸發方法(set)
- function.setter在屬性被賦值的時候被自動執行
- 在使用setter方法的時候必須要一個一個位置參數來接收屬性被賦予的值
正確的姿勢
class Foo(object): name = "CHEN" def __init__(self,name): self.name = name @property def py_print(self): print("this is you‘re name : %s"%self.name) @py_print.setter def py_print(self,age): print("LeonYan was %s years old"%age) f = Foo(‘LeonYan‘) f.py_print = "20"
二、內置方法
2.1:__doc__方法
class Foo(object): """這個是一個空的類""" def func(self): pass print(Foo.__doc__)
2.2 : __module__ 和__class__
__module__ 表示當前操作的對象在那個模塊
__class__ 表示當前操作的對象的類是什麽
from lib.aa import C obj = C() print obj.__module__ # 輸出 lib.aa,即:輸出模塊 print obj.__class__ # 輸出 lib.aa.C,即:輸出類
2.3 __call__
__call__:方法在類被實例化後,直接調用對象執行__call__方法
class Foo(object): def dd(self): pass def __call__(self, *args, **kwargs): print("running function call") f = Foo() f() 打印running function call
三、源類
_new_ 和 __metaclass__(類的創建過程)
在介紹這個方法的時候我先寫一段最簡單的類
class Foo():
def __init__(self,name)
self.name = name
F = Foo("Leonyan")
在python中我們都聽過一切皆對象這個概念,理解起來就是所有東西都可以用點的方式來調用。就拿上面的示例來說F就是有Foo實例化的所得到的對象,這一點沒有任何問題。但是Foo這個類又是通過什麽得來的呢?
print(type(f)) #---><class ‘__main__.Foo‘> print(type(Foo)) #----><class ‘type‘>
我擦通過type看類和對象的類型我們發現Foo這個類竟然是有type創建的?type不是python中的內置函數嗎?其實type是一切的根源,包括類也是它來創建的。但是如果你問type又是什麽創建的,我只能說你沒有靈性了,因為你這就是問了一個雞生蛋的故事了。
下面我們用type創建一個類:
def leonyan(food): print("I am eta %s"%food) Test = type("Test",(object,),{"leonyan":leonyan}) print(Test) Test.leonyan("abc")
- leonyan是為了方便加入到類中的屬性
- type(“Test”)是類名,為了方面理解直接將等號前面也命名為Test,方面在實例化後調用不迷惑
- (object):繼承的類因為可以繼承多個類多以是元祖格式
- {}:裏面是這個類的屬性和變量
我們再將這個方法實例化看一看
def __init__(self,name,age,like): self.name = name self.age = age self.like =like def leonyan(self,food): print("I am eta %s"%food) Test = type("Test",(object,),{"__init__":__init__,"leonyan":leonyan}) T = Test("leonyan",‘18‘,‘girl‘) T.leonyan("rou")
實例化誤區:定義類屬性的時候需要加上一個self的位置變量,因為在實例化的時候我們調用實例點方法的方式的時候默認會把自己當做第一個參數傳到這個方法當中。
__new__類最開始加載的方法,意思是在我們實例化類的時候並不是首先運行的__init__方法而是__new__方法,再通過new方法調用init方法:
class Leon(object): def __init__(self,age): self.age = age def __new__(cls, *args, **kwargs): print("ha ha __init__ function is not start") L = Leon("18") 輸出的結果是ha ha __init__ function is not start
寫到這裏或許不太相信這個類沒有運行init方法,這個時候我們做了一次測試:如果init方法被執行了的話我們在調用L.age的時候肯定是有對應的值得因為我們在實例化的時候傳入了這個值,通過下面的示例就就可以看出在實例化的過程中根本就沒有執行init方法。
new的用法:如果我們自己重新構造了new函數,但是還是要實現原來new方法的功能。
class Leon(object): def __init__(self,age): self.age = age def __new__(cls, *args, **kwargs): print("ha ha __init__ function is not start",*args,**kwargs) obj = object.__new__(cls) print("obj",obj) return obj L = Leon("18") print(L) print(L.age)
- 首先我們看到被實例化的L其實就是return的obj
-
object.__new__(cls)固定寫法,cls是類本身
- return了上面的obj其實就執行的這個類原來的new函數
- 註意上面重新構造的obj其實和已經實例化後的L不是一個東西,雖然他們的內存地址相同,但是通過dir()測試我們會發現obj這個對象中並沒有age這個屬性
非常難理解的__metaclass__
到現在我們已經知道類是有type實例化產生,type類中如何實現創建類?類又是如何創建對象?類中有一個屬性_-metaclass__,其用來表示該類由,誰來實例化創建,所以我們可以為__metaclass__設置type類的派生類,從而查看類創建的過程。
class MyType(type): def __init__(self,*args,**kwargs): print("MyType __init__下面:",*args,**kwargs) def __new__(cls, *args, **kwargs): print("MyType __new__下面:",*args,**kwargs) obj = type.__new__(cls,*args,**kwargs) return obj class Foo(object,metaclass=MyType): def __init__(self,name): self.name = name print("Foo __init__") f = Foo(‘Leonyan‘) print(f.name)
- 一個最基本的類是由new方法去調用init方法,而new又是有type來創建的
- Foo我們可以身為是幾個基類,他繼承了object和MyType也就是我們得源類
- MyType可以控制我們Foo的創建過程
- MyType(源類)也是從new方法開始執行,將結果返回給實例
在上面示例中Foo其實是MyType的實例也就是說為什麽我們可以在Foo後面加上()括號的原因,具體可以使用type(Foo)測試。但是新的問題又來了,如果Foo就是一個實例,那為什麽還可以加上括號去運行呢?這裏可能會有人想到call方法,沒錯call方法可以一個實例加上括號去運行,而且MyType也封裝了它,導致Foo可以執行。
class MyType(type): def __init__(self,*args,**kwargs): print("MyType __init__下面:",*args,**kwargs) def __call__(self, *args, **kwargs): print("MyType Call",*args,**kwargs) obj = self.__new__(self) obj.age = 22 self.__init__(obj,*args,**kwargs) return obj def __new__(cls, *args, **kwargs): print("MyType __new__下面:",*args,**kwargs) obj = type.__new__(cls,*args,**kwargs) return obj class Foo(object,metaclass=MyType): def __init__(self,name): self.name = name print("Foo __init__") # f = Foo(‘Leonyan‘) print(f.name,f.age)深入
四、異常處理
在編程過程中為了增加友好性,在程序出現bug時一般不會將錯誤信息顯示給用戶,而是現實一個提示的頁面,通俗來說就是不讓用戶看見大黃頁!!!
4.1、異常種類
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x IOError 輸入/輸出異常;基本上是無法打開文件 ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊 IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5] KeyError 試圖訪問字典裏不存在的鍵 KeyboardInterrupt Ctrl+C被按下 NameError 使用一個還未被賦予對象的變量 SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了) TypeError 傳入對象類型與要求的不符合 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量, 導致你以為正在訪問它 ValueError 傳入一個調用者不期望的值,即使值的類型是正確的常用異常
dic = ["wupeiqi", ‘alex‘] try: dic[10] except IndexError, e: print e觸發異常
4.2、主動觸發異常
try: raise Exception(‘錯誤了。。。‘) except Exception,e: print e
4.3、自定義異常
class WupeiqiException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise WupeiqiException(‘我的異常‘) except WupeiqiException,e: print e
5.FTP程序:詳見GitHub
面向對象高級-day8