一、 面向對象特性之多態:
上一篇已經介紹了面向對象的三大特性的前兩種(封裝、繼承),下面來說說第三種多態,在python中用不上,但在其他語言中很重要。
多態的意思就是多種類型、多種形態,比如字符類型,數字,字典,列表等。在python中定義類或函數時不需要,指定是那種數據類型全都支持,但是在java、c#等其他語言中需要指定如下實例:
def func(A arg)
print(arg)
arg :參數必須是A 類型,或A類型的子類
二、類成員:
類成員分為三大類:字段、方法、屬性

1、字段
在類中字段分為靜態字段和普通字段
普通字段(動態字段):存儲在對象中,由對象調用
靜態字段:存儲在類中,由類調用,在代碼加載時,就已經創建了
調用規則:
1、一般情況下自己訪問自己;
2、普通字段,只能由對象訪問
3、靜態字段用類訪問,(萬不得已的時候也可以用對象訪問,不建議使用)
兩者區別如下:
class Foo:
CC = 123 # CC是靜態字段,保存在類中
def __init__(self):
self.name = "tom" # name 就是普通字段,保存在對象中
實際調用如下:
class Province:
contry="中國" #靜態字段
def __init__(self,name):
self.name=name #普通字段
sx=Province("河南")
#靜態調用 如 Province.contry
#普通調用 如 sx.name
print(Province.contry,sx.name)
#顯示結果
中國 河南
靜態字段存儲在類中, 只在內存中保存一份;
普通字段存儲在每個對象中,需要在每個對象中都保存一份
應用場景:如果創建對象時,都要需要某一個相同的字段,可以把字段設置為靜態字段,節省內存
2、方法
方法都屬於類包括:
靜態方法:屬於類,由類來調用執行,無默認參數,等同於函數。創建方式: 方法上邊加個@staticmethod
普通方法:由對象調用,至少需要一個self參數。執行普通方法時,self就是對象本身,自動將調用該方法的對象賦值給self
類方法:是靜態方法的一種特殊形式。由類調用,至少要有一個參數cls,值為類名。創建方式: @classmethod
class Province:
contry="中國"
def __init__(self,name): #普通方法
self.name=name
def show(self):
print("普通方法")
@classmethod
def class_show(cls): #類方法
print(cls)
@staticmethod
def static_show(): #靜態方法
print("靜態方法")
sx=Province("河南")
#普通方法調用
sx.show()
#類方法調用
Province.class_show()
#靜態方法調用
Province.static_show()
#顯示結果
普通方法
<class '__main__.Province'> #是這個類名
靜態方法
相同點:由於所有的方法都屬於類, 所以在內存中存儲只保存一份。
不同點:由於各種方法的調用方式不同,調用方法時自動傳入的參數不同。
給上面字段一樣,在對象中也是可以調用靜態方法和類方法的。不到萬不得已還是不要用,要遵循變成原則。
3、屬性
屬性就是普通方法的變種
下面來看一下屬性的定義:
class Foo:
def show(self):
print("普通方法")
@property #定義屬性
def prop(self):
pass
#調用
obj=Foo()
obj.show() #調用方法
obj.prop #調用屬性
注意:
定義時,在普通方法的基礎上添加@property裝飾器;僅有一個參數self
調用時,無需加括號
還有一種屬性的定義調用方法:
class Foo():
@property
def price(self):
print('查詢')
@price.setter
def price(self, value):
print('設置')
@price.deleter
def price(self):
print('刪除')
# ############### 調用 ###############
obj = Foo()
obj.price # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值
obj.price = 123 # 自動執行 @price.setter 修飾的 price 方法,並將 123 賦值給方法的參數
del obj.price # 自動執行 @price.deleter 修飾的 price 方法
#執行結果
查詢
設置
刪除
三、類成員修飾符
公有成員:就是在哪都能訪問,
私有成員,只有在類的內部才能放問,其他都不能訪問,繼承關系也不能
定義私有成員:命名是前面是兩個下劃線,(特殊成員除外,例如:__init__、__call__、__dict__等)
如下:
class Foo:
def __init__(self):
self.name="公有字段"
self.__fuck="私有字段"
四、類的特殊成員
1、__init__
構造方法,通過類創建對象時,自動觸發
class Foo:
def __init__(self,name):
self.name = name # name 就是普通字段
self.job="IT"
obj=Foo("tom") #自動執行類中的__init__方法
print(obj.name)
print(obj.job)
#結果
tom
IT
2、__doc__
表示類的描述信息,就是注釋

class Foo:
'''
描述信息
'''
def __init__(self,name):
self.name = name # name 就是普通字段
self.job="IT"
print(Foo.__doc__)
#顯示結果
描述信息
View Code
3、__del__
析構方法,用於對象在內存中垃圾回收時,自動觸發執行
4、__call__
在對象後面加括號,執行
對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

class Foo:
def __init__(self,name):
self.name = name # name 就是普通字段
def __call__(self, *args, **kwargs):
print("執行call方法")
obj=Foo("test") #執行__init__方法
obj() #執行__call__方法
View Code
5、__module__、__class__
__module__表示當前操作的對象在那個模塊
__class__ 表示當前操作的對象的類是誰

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class Foo:
'''
這是類的描述信息, 由特殊成員 __doc__調用
'''
def __init__(self,name):
self.name = name
# print(self.name)
def show_info(self):
print(self.name)
bin/modules.py內容
bin/modules.py

from bin import modules
obj = modules.Foo('DBQ')
print(obj.__module__) # 查看當前操作的對象屬於哪個模塊
print(obj.__class__) #查看當前操作的對象的類是哪個
#執行結果:
bin.modules
<class 'bin.modules.F1'>
View Code
6、__dict__
獲取對象或類中的所有成員

class Foo:
CC="test"
def __init__(self,name):
self.name = name # name 就是普通字段
#獲取類中的所有成員
print(Foo.__dict__)
#獲取對象中的成員
obj=Foo("tom")
print(obj.__dict__)
#顯示結果
{'__weakref__': <attribute '__weakref__' of 'Foo' objects>, 'CC': 'test', '__doc__': None, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__init__': <function Foo.__init__ at 0x00000041DAED61E0>}
{'name': 'tom'}
View Code
7、__str__
如果一個類中定義了__str__方法,那麼打印對象時,默認輸出該方法的返回值

class Foo:
CC="test"
def __init__(self,name):
self.name = name # name 就是普通字段
def __str__(self):
return "hi tom"
obj=Foo("tom")
print(obj)
#結果
hi tom
View Code
8、__iter__
用於迭代器,之所以列表、字典、元組可以進行for循環,是因為類型內部定義了__iter__

class Foo():
def __init__(self, sq):
self.sq = sq
def __iter__(self):
return iter(self.sq)
obj = Foo([11,22,33,44])
for i in obj:
print(i)
View Code
9、__getitem__、__setitem__、__deltiem__
用於索引操作, 如字典。 分別表示獲取、設置、刪除數據。

class Foo():
def __getitem__(self, item):
print("執行get操作,調用__getitem__方法")
def __setitem__(self, key, value):
print("執行set操作,調用__setitem__方法")
def __delitem__(self, key):
print("執行del操作,調用__delitem__方法")
obj=Foo()
obj['a1'] #get操作,自動觸發__getitem__方法
obj["a1"]=[1,2,3,4,5] #set操作,自動調用__setitem__方法"
del obj['a1'] #del操作,自動調用__delitem__方法
#結果
執行get操作,調用__getitem__方法
執行set操作,調用__setitem__方法
執行del操作,調用__delitem__方法
View Code
10、__getslice__、__setslice__、__delslice__
用於分片操作

class Foo(object):
def __getslice__(self, i, j):
print '__getslice__',i,j
def __setslice__(self, i, j, sequence):
print '__setslice__',i,j
def __delslice__(self, i, j):
print '__delslice__',i,j
obj = Foo()
obj[-1:1] # 自動觸發執行 __getslice__
obj[0:1] = [11,22,33,44] # 自動觸發執行 __setslice__
del obj[0:2] # 自動觸發執行 __delslice__
python2.7執行
在python3中還是運用的__getitem__、__setitem__、__deltiem__

class Foo():
def __getslice__(self, i, j):
print('__getslice__',i,j)
def __setslice__(self, i, j, sequence):
print('__setslice__',i,j)
def __delslice__(self, i, j):
print('__delslice__',i,j)
def __getitem__(self, item):
print("執行get操作,調用__getitem__方法")
def __setitem__(self, key, value):
print("執行set操作,調用__setitem__方法")
def __delitem__(self, key):
print("執行del操作,調用__delitem__方法")
obj = Foo()
obj[-1:1] # 自動觸發執行 __getslice__
obj[0:1] = [11,22,33,44] # 自動觸發執行 __setslice__
del obj[0:2] # 自動觸發執行 __delslice__
#結果
執行get操作,調用__getitem__方法
執行set操作,調用__setitem__方法
執行del操作,調用__delitem__方法
python3中
11. __new__ 、 __metaclass__
class F1:
def __init__(self,name):
self.name = name
def show_info(self):
print(self.name)
obj = F1('tom')
print(type(obj))
print(type(F1))
# #執行代碼結果:
# <class '__main__.F1'> #表示 obj對象由 F1類實例化而來
# <class 'type'> #表示 F1類由 type 類創建
python中一切介對象,上述代碼中obj是F1的一個對象,那麼可以推理F1其實也是一個對象
所以,obj對象時F1類的一個實例,F1類對象時type類的一個實例,F1類對象時通過type類構造方法創建
創建方法有兩種普通方法、特殊方法:
普通方法:
class F1:
def __init__(self,name):
self.name = name
def show_info(self):
print(self.name)
特殊方法:
def show_info(self):
print('tom')
F1 = type('F1',(object,),{'show_info':show_info})
#第一個參數: 類名
#第二個參數: 當前類的基類
#第三個參數: 類成員
obj = F1()
obj.show_info()
==》 類 是由 type 類實例化產生
那麼問題來了,類默認是由 type 類實例化產生,type類中如何實現的創建類?類又是如何創建對象?
答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來實例化創建,所以,我們可以為 __metaclass__ 設置一個type類的派生類,從而查看 類 創建的過程。

五、面向對象其他相關知識
1、isinstance(obj,cls)
檢查是否obj是否是類 cls 的對象
判斷一個對象是不是類創建的()實例,返回布爾值,繼承的父類也為真
class F1:
pass
class F2(F1):
pass
obj = F2()
print(isinstance(obj,F1)) #查看是否是父類的實例, 為真
print(isinstance(obj,F2)) #查看是否是F2類的實例, 為真
2、issubclass(F1,F2)
檢查F1是否是F2的子類
查看是否是某類的子類
class F1:
pass
class F2(F1):
pass
obj = F2()
print(issubclass(F2,F1)) #查看F2是否是F1的子類, 為真
print(issubclass(F1,F2)) #查看F1是否是F2的子類, 為假
3、 super
擴展別人的源碼 ,盡量不在源碼中修改
class C1:
def f1(self):
print('C1.f1')
class C2(C1):
def f1(self):
super(C2,self).f1() #在執行C2代碼之前,執行C1中的f1方法 也就是C2父類的f1方法
print('C2.f1')
obj = C2()
obj.f1()
#結果
C1.f1
C2.f1
五、異常處理與捕獲
1、異常處理基礎
增加友好性,在程序出現bug中一般不會將錯誤信息顯示給用戶,而是顯示一個頁面
while True:
num = input('請輸入你一個或多個整數: ').strip()
try:
num = int(num)
print('你輸入的數字是: %d'%num)
except Exception:
print('%s, 你輸入的不是一個整數格式!'%Exception)
# 如果輸入的是一個整數類型,將返回輸入的號碼
# 如果輸入的是其他的類型,如字符串、浮點數等,會提示用戶輸入的不是一個整數格式!
####執行結果:
請輸入你一個或多個整數: 123
你輸入的數字是: 123
請輸入你一個或多個整數: a
<class 'Exception'>, 你輸入的不是一個整數格式!
請輸入你一個或多個整數: 1.
<class 'Exception'>, 你輸入
2.異常處理
捕獲異常可以使用 try / except語句。try: 用來檢測語句塊中的錯誤,從而讓 except中語句捕獲的異常信息並處理。
打開一個文件,往文件中寫入內容,並且沒有發生異常:
try:
f = open('test.txt','w')
f.write('測試文件,用於測試異常捕獲')
except IOError:
print('Error: 寫入失敗, 沒有找到文件或者權限不足!')
else:
print('寫入成功!')
f.close()
#執行結果:
寫入成功!
#文件內容:
Daniel-Mac:blog daniel$ cat test.txt &&echo
測試文件,用於測試異常捕獲
修改文件的權限沒有寫,而後在打開文件,往文件中寫入內容,查看異常:
chmod -w test.txt
try:
f = open('test.txt','w')
f.write('測試文件,用於測試異常捕獲')
except IOError:
print('Error: 寫入失敗, 沒有找到文件或者權限不足!')
else:
print('寫入成功!')
f.close()
#再次執行代碼:
Error: 寫入失敗, 沒有找到文件或者權限不足!
常用異常:
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x IOError 輸入/輸出異常;基本上是無法打開文件 ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊 IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5] KeyError 試圖訪問字典裡不存在的鍵 KeyboardInterrupt Ctrl+C被按下 NameError 使用一個還未被賦予對象的變量 SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了) TypeError 傳入對象類型與要求的不符合 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量, 導致你以為正在訪問它 ValueError 傳入一個調用者不期望的值,即使值的類型是正確的
實例:
下標異常
dic = ["tom", 'jerry']
try:
dic[10]
except IndexError, e:
print e
key異常
dic = {'k1':'v1'}
try:
dic['k20']
except KeyError, e:
print e
元素異常
s1 = 'hello'
try:
int(s1)
except ValueError, e:
print e
對於上述實例,異常類只能用來處理指定的異常情況,如果沒有指定異常則無法處理。
# 未捕獲到異常,程序直接報錯
tt = 234
try:
str(tt)
except IndexError,e:
print e
如果想通吃各種異常,python中也提供了一個萬能異常Exception,就能捕獲任意異常,目的是保證程序能正常運行
tt = 234
try:
str(tt)
except Exception,e:
print e
如果你還想要知道異常是什麼,在那一塊報錯了,還有一個更周全的方案,如下:
s1 = 'hello world'
try:
int(s1)
except KeyError:
print('Error: Key錯誤!')
except IndexError:
print('Error: 索引錯誤!')
except ValueError:
print('Error: 值錯誤!')
except Exception:
print('Error: 出錯了!')
else:
print('你的值是: %s'%s1)
六、設計模式,單實例
例模式,顧名思義,也就是單個實例的意思。
模式特點:保證類僅有一個實例,並提供一個訪問它的全局訪問點。

class Singleton:
__instance = None #定義一個私有靜態字段為初始值
def __init__(self,name):
self.name = name
def show(self):
print(self.name)
return 'test_instance'
@classmethod
def get_instance(cls):
if cls.__instance: #如果字段內有值,直接返回字段值
return cls.__instance
else:
obj = cls('DBQ') #實例化
cls.__instance = obj #將對象賦值給字段
return cls.__instance #返回對象
a = Singleton.get_instance()
b = Singleton.get_instance()
print(a)
print(id(a)) #內存地址和b相同
print()
print(b)
print(id(b)) #內存地址和a相同
# 後面再來幾個對象,也是一樣的!
#代碼執行結果:
<__main__.Singleton object at 0x101b769b0>
<__main__.Singleton object at 0x101b769b0>
單實例
單例模式的存在主要是保證當前內存中存在單個實例,避免內存資源浪費。