面向對象編程是一種編程方式,此編程方式的落地須要使用 「類」 和 「對象」 來實現,因此,面向對象編程其實就是對 「類」 和 「對象」 的使用。php
類就是一個模板,模板裏能夠包含多個函數,函數裏實現一些功能python
對象則是根據模板建立的實例,經過實例對象能夠執行類中的函數程序員
class是關鍵字,表示類建立對象,類名稱後加括號便可編程
注:類中的函數第一個參數必須是self(詳細見:類的三大特性之封裝) ide
類中定義的函數叫作 「方法」函數
#建立類
class Foo:
def Bar(self):
print("Bar")
def Hello(self,name):
print("I love %s" %name)
#根據Foo類建立obj對象
obj = Foo()
obj.Bar() #執行Bar方法
obj.Hello('python') #執行Hello方法
self參數至關於php面向對象中的$this,誰調用它就指向誰fetch
面向對象的三大特性是指:封裝、繼承和多態。this
1、封裝spa
封裝,顧名思義就是將內容封裝到某個地方,之後再去調用被封裝在某處的內容。指針
因此,在使用面向對象的封裝特性時,須要:
第一步:將內容封裝到某處
class Foo:
def __init__(self,name,age):##__init__稱爲構造方法,根據類建立對象時自動執行
self.name = name
self.age = age
obj1 = Foo('poe',21)##poe and 21分別封裝到obj1 self的name and age屬性中
print(obj1.name,obj1.age)
obj2 = Foo('jet',22)##jet and 22分別封裝到obj2 self的name and age屬性中
print(obj2.name,obj2.age)
self 是一個形式參數,當執行 obj1 = Foo('wupeiqi', 18 ) 時,self 等於 obj1
當執行 obj2 = Foo('alex', 78 ) 時,self 等於 obj2
因此,內容其實被封裝到了對象 obj1 和 obj2 中,每一個對象中都有 name 和 age 屬性,在內存裏相似於下圖來保存。
第二步:從某處調用被封裝的內容
調用被封裝的內容時,有兩種狀況:
一、經過對象直接調用被封裝的內容
上圖展現了對象 obj1 和 obj2 在內存中保存的方式,根據保存格式能夠如此調用被封裝的內容:對象.屬性名
class Foo:
def __init__(self,name,age):##__init__稱爲構造方法,根據類建立對象時自動執行
self.name = name
self.age = age
obj1 = Foo('poe',21)##poe and 21分別封裝到obj1 self的name and age屬性中
print(obj1.name,obj1.age)# 直接調用obj1對象的name和age屬性
obj2 = Foo('jet',22)##jet and 22分別封裝到obj2 self的name and age屬性中
print(obj2.name,obj2.age)# 直接調用obj2對象的name和age屬性
二、經過self間接調用被封裝的內容
執行類中的方法時,須要經過self間接調用被封裝的內容
class Foo:
def __init__(self,name,age):##__init__稱爲構造方法,根據類建立對象時自動執行
self.name = name
self.age = age
def detail(self):
print(self.name)
print(self.age)
obj1 = Foo("poe",21)
obj1.detail()# Python默認會將obj1傳給self參數,即:obj1.detail(obj1),因此,此時方法內部的 self = obj1,即:self.name 是 poe ;self.age 是 21
obj2 = Foo("jet",22)
obj2.detail()# Python默認會將obj1傳給self參數,即:obj1.detail(obj1),因此,此時方法內部的 self = obj1,即:self.name 是 jet ;self.age 是 22
綜上所述,對於面向對象的封裝來講,其實就是使用構造方法將內容封裝到 對象 中,而後經過對象直接或者self間接獲取被封裝的內容。
2、繼承
繼承,面向對象中的繼承和現實生活中的繼承相同,即:子能夠繼承父的內容。
class Animal:
def eat(self):
print("%s eat" %self.name)
def drink(self):
print("%s drink" %self.name)
class Cat(Animal):
def __init__(self,name):
self.name = name
self.breed = 'cat'
def cry(self):
print("cry")
class Dog(Animal):
def __init__(self,name):
self.name = name
self.breed = 'dog'
def cry(self):
print("cry")
c1 = Cat('little cat')
c1.eat()
c2 = Cat('big cat')
c2.eat()
d1 = Dog('little black')
d1.eat()
因此,對於面向對象的繼承來講,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而沒必要一一實現每一個方法。
注:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不一樣而已。
那麼問題又來了,多繼承呢?
一、Python的類能夠繼承多個類,Java和C#中則只能繼承一個類
二、Python的類若是繼承了多個類,那麼其尋找方法的方式以下圖:
假設G類在繼承時C類在前,F類在後,如:G(C,F)
黑色的箭頭表示繼承關係:B類繼承A類,C類繼承B類,D類繼承A類,F類繼承D類,G類繼承C類與F類
橘黃色箭頭表示查找順序,上圖中的查找順序爲:G->C->B->F->D->A
若是A類沒有的繼承關係,那麼查找順序以下圖:
class Animal:
def eat(self):
print("%s eat" %self.name)
def drink(self):
print("%s drink" %self.name)
def piao(self):
print("Animal piao")
class Uncle:
def piao(self):
print('Uncle piao')
def du(self):
print('du')
class Cat(Uncle,Animal):#繼承了Uncle類和Animal類
def __init__(self,name):
self.name = name
self.breed = 'cat'
def cry(self):
print("cry")
c1 = Cat('little cat')
c1.piao()
c2 = Cat('big cat')
c2.piao()
上面的Cat類繼承了Unlcle類和Animal類,兩個類中又都有piao()方法,那麼在調用該方法的時候,是按什麼順序執行的呢?執行哪一個類裏面的piao()方法呢?
1:若是Cat類自身中有piao()方法,那就在調用piao()方法時先執行自身類中的piao()方法
2:若是Cat類自身中沒有piao()方法,像上面的代碼,就按繼承時的順序執行,class Cat(Uncle,Animal),這兩個類哪一個寫在前面,就優先執行哪一個類中的piao()方法
3、多態
Pyhon不支持Java和C#這一類強類型語言中多態的寫法,可是原生多態,其Python崇尚「鴨子類型」。
class F1:
pass
class S1(F1):
def show(self):
print('S1.show')
class S2(F1):
def show(self):
print("S2.show")
def Func(obj):
print(obj.show())
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
擴展:
重載:函數名相同,參數個數不一樣(python不支持)
重寫:派生類中從新實現基類中的方法
接口:python中沒有接口這一說
類和對象在內存中是如何保存?
類以及類中的方法在內存中只有一份,而根據類建立的每個對象都在內存中須要存一份,大體以下圖:
如上圖所示,根據類建立對象時,對象中除了封裝 name 和 age 的值以外,還會保存一個類對象指針,該值指向當前對象的類。
當經過 obj1 執行 【方法一】 時,過程以下:
class A: def __init__(self): print('A contruct') class B(A): def __init__(self): print('B contruct') super(B,self).__init__()#推薦使用此方法 #A.__init__(self)#此方法也能夠執行基類的構造方法 obj = B()
class Foo: def __init__(self,name): self.name = name def show(self): print('show') obj = Foo('poe') #反射:類,只能找到類裏的成員 #反射:對象,既能夠找對象,也能夠找類的成員 print(hasattr(Foo,'show')) print(hasattr(obj,'name'))
##commons.py文件 class Foo: def __init__(self,name): self.name = name gender = 'male' def show(self): print('show') ##index.py文件 #導入模塊 m = __import__('commons',fromlist = True) #找到模塊中的類 class_name = getattr(m,'Foo') #根據類建立對象 obj = class_name("bruce") #利用對象去找name的值 val = getattr(obj,'name') print(val)
一個類中存在普通字段,靜態字段,普通方法,靜態方法,類方法那麼在訪問這些成員的時候優先選擇本身的成員本身去訪問:
經過類去訪問的有:靜態字段,靜態方法,類方法
經過對象去訪問的有:普通字段,普通方法
但請注意:這麼作只是爲了代碼的規範,這四個成員利用對象都是能夠訪問到的
class Province: #靜態字段,保存類中,內存中只有一個 country = "China" def __init__(self,name): #普通字段,保存對象中!有幾個對象,就有幾個字段 self.name = name #普通方法,保存在類中 def show(self): print('show') #靜態方法,靜態方法中不須要self參數 @staticmethod def xo(arg1,arg2): return arg1+arg2 #類方法,類方法也不須要self參數,但要有一個cls參數(class) @classmethod def xxoo(cls): print("xxoo",cls) hunan = Province('hunan') hunan.show() print(hunan.xo(3,4)) print(Province.country) print(Province.xo(1,2)) Province.xxoo()#調用類方法,cls參數會自動得到當前類的類名
class Province: def start(self): temp = "%s is hero" %self.name return temp #特性,將方法僞形成一種字段 @property def end(self): temp = "%s is hero" %self.name return temp obj = Province('poe') print(obj.start()) print(obj.end)#訪問特性時,後面不須要加括號,因此也就沒法傳遞參數
類的特性默認狀況下是沒法像普通字段那樣在類外進行從新賦值,若是須要對特性進行從新賦值,須要用到一個裝飾器:
class Province: #靜態字段,保存類中,內存中只有一個 country = "China" def __init__(self,name): #普通字段,保存對象中!有幾個對象,就有幾個字段 self.name = name #普通方法,保存在類中 def show(self): print('show') #靜態方法,靜態方法中不須要self參數 @staticmethod def xo(arg1,arg2): return arg1+arg2 #類方法,類方法也不須要self參數,但要有一個cls參數(class) @classmethod def xxoo(cls): print("xxoo",cls) def start(self): temp = "%s is hero" %self.name return temp #特性,將方法僞形成一種字段 @property def end(self): temp = "%s is hero" %self.name return temp @end.setter def end(self,value): print(value) self.name = value obj = Province('poe') p = obj.end print(p) #設置特性 obj.end = "bruce" print(obj.end) ################################################ poe is hero bruce bruce is hero
快速判斷成員由類執行仍是對象執行:
有self的,對象調用,無self的類調用
對於每個類的成員而言都有兩種形式:
私有成員和公有成員的定義不一樣:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)
class A: def __init__(self): self.name = 'public' self.__nick = 'private' obj = A() print(obj.name) print(obj.__nick) ################################################## public Traceback (most recent call last): File "index.py", line 11, in <module> print(obj.__nick) AttributeError: 'A' object has no attribute '__nick'
如何訪問類中的靜態成員:兩種方法
法一:在類中添加一個方法
class A: def __init__(self): self.name = 'public' self.__nick = 'private' def fetch(self): print(self.__nick) obj = A() obj.fetch()
法二:使用python的特定語法(此方法不推薦使用)
class A: def __init__(self): self.name = 'public' self.__nick = 'private' obj = A() print(obj._A__nick)#A前面一個下劃線
構造方法,經過類建立對象時,自動觸發執行。
class Foo: def __init__(self, name): self.name = name self.age = 18 obj = Foo('poe') # 自動執行類中的 __init__ 方法
析構方法,當對象在內存中被釋放時,自動觸發執行。
注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
class Foo: def __del__(self): pass
注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
class Foo: def __init__(self): print('init') # __call__方法用於實例自身的調用 def __call__(self,*args,**kwargs): print('call') return 1 # obj = Foo() # obj() r = Foo()() print(r) ################################################## init call 1
用於索引操做,如字典。以上分別表示獲取、設置、刪除數據
class Foo: def __getitem__(self,item): print(item) def __setitem__(self,key,value): print(key,value) def __delitem__(self,key): print(key) obj = Foo() obj['kk'] obj['aa'] = 123 del obj['kk'] obj['kk']
class Foo: def __init__(self): self.name = 'poe' def __call__(self): print('call') def __getitem__(self,item): print(item) def __setitem__(self,key,value): print(key,value) def __delitem__(self,key): print(key) obj = Foo() #__init__ print(obj.__dict__) print(Foo.__dict__)
用於迭代器,之因此列表、字典、元組能夠進行for循環,是由於類型內部定義了 __iter__
class Foo: def __iter__(self): yield 1 yield 2 yield 3 obj = Foo() for i in obj: print(i)
閱讀如下代碼:
class Foo(object): def __init__(self): pass obj = Foo() # obj是經過Foo類實例化的對象
上述代碼中,obj 是經過 Foo 類實例化的對象,其實,不只 obj 是一個對象,Foo類自己也是一個對象,由於在Python中一切事物都是對象。
若是按照一切事物都是對象的理論:obj對象是經過執行Foo類的構造方法建立,那麼Foo類對象應該也是經過執行某個類的 構造方法 建立。
print type(obj) # 輸出:<class '__main__.Foo'> 表示,obj 對象由Foo類建立 print type(Foo) # 輸出:<type 'type'> 表示,Foo類對象由 type 類建立
因此,obj對象是Foo類的一個實例,Foo類對象是 type 類的一個實例,即:Foo類對象 是經過type類的構造方法建立。
那麼,建立類就能夠有兩種方式:
a). 普通方式
class Foo(object): def func(self): print('hello python')
b).特殊方式(type類的構造函數)
def func(self): print('hello python') Foo = type('Foo',(object,),{'func':func}) print(Foo,type(Foo)) #type第一個參數:類名 #type第二個參數:當前類的基類 #type第三個參數:類的成員
那麼問題來了,類默認是由 type 類實例化產生,type類中如何實現的建立類?類又是如何建立對象?
答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來實例化建立,因此,咱們能夠爲 __metaclass__ 設置一個type類的派生類,從而查看 類 建立的過程。
表示類的描述信息
class Foo: """ 描述類信息,這是用於看片的神奇 """ def func(self): pass print Foo.__doc__ #輸出:類的描述信息
若是一個類中定義了__str__方法,那麼在打印 對象 時,默認輸出該方法的返回值。
class Foo: def __str__(self): return('poe') obj = Foo() print(obj) # 輸出:poe
__module__ 表示當前操做的對象在那個模塊
__class__ 表示當前操做的對象的類是什麼
# lib/commons.py class C: def __init__(self): self.name = 'poe'
# index.py from lib.commons import C obj = C() print(obj.__module__)# 輸出 lib.commons,即:輸出模塊 print(obj.__class__)# 輸出 lib.commons.C,即:輸出類
一、異常基礎
在編程過程當中爲了增長友好性,在程序出現bug時通常不會將錯誤信息顯示給用戶,而是顯示一個提示的頁面,通俗來講就是不讓用戶看見大黃頁!!!
try: pass except Exception,ex: pass
需求:將用戶輸入的兩個數字相加
while True: num1 = raw_input('num1:') num2 = raw_input('num2:') try: num1 = int(num1) num2 = int(num2) result = num1 + num2 except Exception, e: print '出現異常,信息以下:' print e
二、異常種類
python中的異常種類很是多,每一個異常專門用於處理某一項異常!!!
AttributeError 試圖訪問一個對象沒有的屬性,好比foo.x,可是foo沒有屬性x IOError 輸入/輸出異常;基本上是沒法打開文件 ImportError 沒法引入模塊或包;基本上是路徑問題或名稱錯誤 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊 IndexError 下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5] KeyError 試圖訪問字典裏不存在的鍵 KeyboardInterrupt Ctrl+C被按下 NameError 使用一個還未被賦予對象的變量 SyntaxError Python代碼非法,代碼不能編譯(我的認爲這是語法錯誤,寫錯了) TypeError 傳入對象類型與要求的不符合 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是因爲另有一個同名的全局變量, 致使你覺得正在訪問它 ValueError 傳入一個調用者不指望的值,即便值的類型是正確的
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
更多異常
dic = ["Jet", 'Jacky'] try: dic[10] except IndexError, e: print e
s1 = 'hello' try: int(s1) except ValueError, e: print e
對於上述實例,異常類只能用來處理指定的異常狀況,若是非指定異常則沒法處理。
# 未捕獲到異常,程序直接報錯 s1 = 'hello' try: int(s1) except IndexError,e: print e
因此,寫程序時須要考慮到try代碼塊中可能出現的任意異常,能夠這樣寫:
s1 = 'hello' try: int(s1) except IndexError,e: print e except KeyError,e: print e except ValueError,e: print e
萬能異常 在python的異常中,有一個萬能異常:Exception,他能夠捕獲任意異常,即:
s1 = 'hello' try: int(s1) except Exception,e: print e
接下來你可能要問了,既然有這個萬能異常,其餘異常是否是就能夠忽略了!
答:固然不是,對於特殊處理或提醒的異常須要先定義,最後定義Exception來確保程序正常運行。
s1 = 'hello' try: int(s1) except KeyError,e: print '鍵錯誤' except IndexError,e: print '索引錯誤' except Exception, e: print '錯誤'
三、異常其餘結構
try: # 主代碼塊 pass except KeyError,e: # 異常時,執行該塊 pass else: # 主代碼塊執行完,執行該塊 pass finally: # 不管異常與否,最終執行該塊 pass
四、主動觸發異常
try: raise Exception('錯誤了。。。') except Exception,e: print e
五、自定義異常
class MyException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise MyException('個人異常') except MyException,e: print e
六、斷言
# assert 條件 assert 1 == 1 assert 1 == 2