一、面向物件之多型
1、多型:簡而言子就是多種形態或多種型別
python中不支援多型也用不到多型,多型的概念是應用與java/C#中指定傳參的資料型別,
java多型傳參:必須是傳引數的資料型別或傳參的子類型別
面向物件總結:
面向物件是一種程式設計方式,此程式設計方式的實現是基於類和物件的使用
類:是一個模板,模板中包含了多個函式共使用,即類中可包含多個函式(類中的函式即叫做方法)
一般疑問:
1)什麼樣的程式碼才是面向物件?
簡單來說,如果程式中的所有功能否是由 “類”和“物件”實現,那麼就是面向物件程式設計
2)函數語言程式設計和麵向物件如果選擇?分別在什麼情況下使用?
C#和java只支援面向物件程式設計,不支援函數語言程式設計
python和PHP則支援兩種程式設計方式,且函數語言程式設計能完成的操作,面向物件都可以實現;而物件能實現的操作,函式則不行(函數語言程式設計無法實現面向物件的封裝功能)
python程式設計中,全部使用面向物件或面向物件和函式式混合使用
1、多個函式使用共同的值:類中的函式俗稱為一個方法
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:lcj
#Blog address(部落格地址):http://i.cnblogs.com/EditPosts.aspx?postid=5500010&update=1
#學號:stu179501 password:055bc2457dfa2e class hh:
def __init__(self,host,user,pwd):
self.host = host
self.user = user
self.pwd = pwd
#增加
def zengjia(self,sql):
print(self.host)
print(self.user)
print(self.pwd)
print(sql)
#刪除
def shangchu(self,sql):
print(sql)
#修改
def xiugai(self):
print(self.host)
print(self.user)
print(self.pwd)
#查
def cha(self):
pass
#將動態傳輸'localhost','lcj',123傳給__init__方法中host,user,pwd
obj1 = hh('localhost','lcj',123)
# obj1.zengjia('selet * from A') #有物件呼叫方法時系統會自動執行__init方法並將動態引數selet * from A傳遞個sql
##輸出
#localhost
# lcj
# 123
# selet * from A
# obj2 = hh('localhost','lcj',123)
# obj2.shangchu('select * from lcj')
# #修改,host,使用者名稱,密碼
obj3 = hh('127.0.0.1','xiaoluo',123)
obj3.xiugai()
# 127.0.0.1
# xiaoluo
# 123
2、需要建立多個事務,每一個事務屬性個數相同,但是值得需求如:姓名、年齡、血型都不相同,即屬性個數相同,但是值不相同
class dome():
def __init__(self,name,age,xuexing):
self.name = name
self.age = age
self.xuexing = xuexing
def tail(self):
temp = "My nian%s age%s 血型%s"%(self.name,self.age,self.xuexing) #指定站位符記性傳參
print(temp)
obj1 = dome('lcj',12,'O')
obj1.tail()
# My nianlcj age12 血型O
obj2 = dome('xiaoluo',18,'O')
obj2.tail()
# My nianxiaoluo age18 血型O
二、面向物件中類成員
python類成員包含:欄位、方法、屬性
1、欄位
普通欄位:儲存在物件中 ,存在於物件中,一般情況下由物件訪問普通欄位
靜態欄位:屬於類中,儲存在內裡面,即在類下面,由“類”訪問靜態欄位
注意:一般情況下,自己訪問自己,靜態欄位中(在萬不得已情況下可以用物件訪問靜態欄位)
靜態欄位在程式碼載入時已經建立,而物件則需要建立並呼叫才能將資料存放在記憶體
由圖可知:
- 靜態欄位在記憶體中只儲存一份
- 普通欄位在每一個物件中都要儲存
應用場景:通過類建立物件時,如果每一個物件中都包含相同的欄位,那麼就可使用靜態欄位(靜態欄位存在於類面)
class Province:
#靜態欄位
Country = '世界'
def __init__(self,name):
#普通欄位
self.name = name
#一般情況下,自己訪問自己
zh = Province('中國')
print(zh.name) #物件訪問普通欄位
#靜態欄位:只有類才能訪問,在萬不得已下可以用物件訪問靜態欄位
print(zh.Country)
#由類訪問靜態欄位
print(Province.Country)
2、方法
python中什麼是方法?
定義:將函式放至類中,且傳參中含有“slef”,表示方法
方法:靜態方法、普通方法及類方法,三種方法在記憶體中都歸屬類,其他語言中只含有兩種方法:靜態方法和普通普通方法
1)靜態方法:
靜態方法:呼叫靜態方法時,需再方法前加@staticmethod,引數即可有可無,一般由類執行靜態方法(不到萬不得已時,可用物件呼叫靜態方法),靜態方法中引數可有可無
class Province:
#靜態欄位
Country = '世界'
def __init__(self,name):
#普通欄位
self.name = name
#普通方法,與物件呼叫(方法屬於類)
def show(self):
print(self.name)
#靜態方法:兩步,去掉self,二,新增裝飾器@staticmethod
@staticmethod
def f1(arg1,arg2):
#靜態方法由類呼叫,(當方法內部不需要物件中封裝的值時,可以將方法寫成靜態方法)
print(arg1,arg2)
# obj1 = Province(456,234)
# obj1.f1() #由物件呼叫靜態方法,則報錯
#類呼叫靜態方法
Province.f1(111,122)
# 111 122
2)普通方法:
建立普通方法時引數中必須帶一個self關鍵字,當任意物件執行普通方法時,自動將呼叫該方法的物件賦值給self
3)類方法:
建立規則:建立類方法規則:普通方法前加@classmethod ,引數中必須帶一個“cls”關鍵字,執行類方法時,自動將呼叫該方法的類賦值給cls關鍵字
特點:由類呼叫類方法時,系統自動將當前類名傳遞給cls引數
class Province:
#靜態欄位
Country = '世界'
def __init__(self,name):
#普通欄位
self.name = name
#普通方法,與物件呼叫(方法屬於類)
def show(self):
print(self.name)
#靜態方法:兩步,去掉self,二,新增裝飾器@staticmethod
@staticmethod
def f1(arg1,arg2):
#靜態方法由類呼叫,
print(arg1,arg2)
#類方法:由類呼叫,
@classmethod
# 類方法引數中至少有“cls"引數
def f2(cls): #呼叫類方法時,系統自動將當前類名傳遞給cls
print(cls)
#類呼叫靜態方法
Province.f1(111,122)
# 111 122
Province.f2()
#<class '__main__.Province'>
3、屬性
定義:不倫不類的東西
規則:在建立屬性時,在普通方法基礎上新增@property裝飾器,屬性僅有一個self引數,呼叫時無需加括號,方法:foo_obj.func(),屬屬性:foo_obj.prop
按照物件呼叫方法進行訪問形式且執行呼叫方法時不加“括號”,ret = obj.show 【obj屬於物件,show屬於物件中存在的方法】
特點:具有方法的寫作形式(),又有欄位的訪問形式(欄位是用過物件訪問,且訪問時不加“括號”進行訪問)
Python的構造方法中有四個引數:
第一個引數是方法名,呼叫 物件.屬性 時自動觸發執行方法
第二個引數是方法名,呼叫 物件.屬性 = XXX 時自動觸發執行方法
第三個引數是方法名,呼叫 del 物件.屬性 時自動觸發執行方法
第四個引數是字串,呼叫 物件.屬性.__doc__ ,此引數是該屬性的描述資訊
class Foo: def get_bar(self):
return 'lcj' # *必須兩個引數
def set_bar(self, value):
return 'set value' + value def del_bar(self):
return 'qaz' BAR = property(get_bar, set_bar, del_bar, 'description...') obj = Foo() ret = obj.BAR # 自動呼叫第一個引數中定義的方法:get_bar
print(ret)
ret= obj.BAR = "xiaoluo" # 自動呼叫第二個引數中定義的方法:set_bar方法,並將“alex”當作引數傳入
print(ret)
del Foo.BAR # 自動呼叫第三個引數中定義的方法:del_bar方法
# obj.BAE.__doc__ # 自動獲取第四個引數中設定的值:description...
通過方法完成分頁:
#通過方法計算分頁
class www:
def __init__(self,all_count):
self.all_count = all_count
def show(self):
#divmod:計算分頁,a1表示商,a2:表示餘數
a1,a2 = divmod(self.all_count,10)
if a2 == 0:
return a1
else:
return a1 + 1
obj = www(101) #建立obj1物件,並把101引數賦值給物件obj
ret = obj.show() #由物件呼叫show方法,並將返回結果值賦值給一個變數
print(ret)
# 11
通過新增屬性@property,再由物件呼叫方法執行
class www:
def __init__(self,all_count):
#欄位all_count
self.all_count = all_count
#屬性
@property
def show(self):
#divmod:計算分頁,a1表示商,a2:表示餘數
a1,a2 = divmod(self.all_count,10)
if a2 == 0:
return a1
else:
return a1 + 1
obj = www(101) #建立obj1物件,並把101引數賦值給物件obj
# ret = obj.all_count #物件呼叫欄位all_count
# print(ret) 101
# ret = obj.show() #由物件呼叫show方法,並將返回結果值賦值給一個變數
# print(ret)
# 11
ret3 = obj.show #通過物件呼叫方法,此時方法已經被屬性化,無需加()即可執行show方法
print(ret3)
# 11
對屬性中的數值進行“修改(新增裝飾器@show.setter)、刪除(新增裝飾器@show.deleter)”操作,show屬於方法
class www:
def __init__(self,all_count):
#欄位all_count
self.all_count = all_count
#屬性
@property
def show(self):
#divmod:計算分頁,a1表示商,a2:表示餘數
a1,a2 = divmod(self.all_count,10)
if a2 == 0:
return a1
else:
return a1 + 1
#修改屬性中的數值
@show.setter
def show(self,value):
print(value)
#對屬性中數值進行刪除
@show.deleter
def show(self):
print('del show')
obj = www(101) #建立obj1物件,並把101引數賦值給物件obj
# ret = obj.all_count #物件呼叫欄位all_count
# print(ret) 101
# ret = obj.show() #由物件呼叫show方法,並將返回結果值賦值給一個變數
# print(ret)
# 11
# print(obj.all_count)
# obj.all_count = 102 #對欄位進行重新賦值
# print(obj.all_count)
# del obj.all_count #刪除欄位中的數字
ret = obj.show
print(ret)
#修改屬性中的數值
obj.show = 123
#刪除屬性中值
del obj.show
方式二:foo = property(F1,F2,F3)
class lcj:
def __init__(self,count):
self.count = count
pass
def F1(self):
return 111
def F2(self,value):
pass
def F3(self):
print('hahaha')
#當物件呼叫foo方法時,系統會自動執行指定的方法
foo = property(F1,F2,F3)
obj = lcj(124)
#列印原屬性中的值
ret = obj.foo
print(ret) # 返回值:111
#修改屬性值
ret = obj.foo = 1000
print(ret) #返回修改屬性值為:1000
del obj.foo #返回刪除屬性中的值:hahaha
三、類成員的修飾符
類成員修飾符可分:公共成功和私有成員
1、什麼是公共成員和私有成員?
公共成員:在任何地方都能訪問,即可在類外面和物件呼叫方法進行訪問
私有成員:只有在類的內部才能訪問
class C: def __init__(self):
self.name = '公有欄位'
self.__foo = "私有欄位"
建立私有成員規則:即在欄位前面新增兩個雙下劃線:__欄位,且只能有內部訪問(通過物件呼叫私有欄位訪問),外部訪問不了
2、私有成員:私有成員命名時,前兩個字元時下劃線,特殊成員除外:如:(__init__、__call__、__dict__等)
即私有欄位:只能類自己本身成員內部可以訪問,其他成員訪問不了
靜態欄位:公有靜態欄位和私有靜態欄位
公有靜態欄位:類可以訪問,類內部可以訪問;派生類中也可訪問
class foo:
name = '公共靜態欄位'
def __init__(self,name):
self.name = name #公有靜態欄位
def f1(self):
print(self.name)
class fol(foo):
def f2(self):
print(self.name)
#類訪問公共靜態欄位
print(foo.name) #公共靜態欄位
#類內部通過物件訪問方法
obj = foo('lcj')
obj.f1() #lcj
#中派生類中可以訪問
obj2 = fol('xiaoluo')
obj2.f2() #先去fol類中執行f2方法,如沒有則在父類foo中執行f2方法
#xiaoluo
私有靜態欄位:僅類內部可以訪問
class foo:
#建立私有靜態欄位
__age = 18
def __init__(self,name):
#建立私有欄位:__name
self.__name = name
def f1(self):
print(self.__name)
#建立靜態方法
@staticmethod
def f2():
print(foo.__age)
foo.f2() #通過類直接呼叫靜態方法中F2方法
3、公有成員:
【不到萬不得已不要在外部強制訪問私有成員】
class foo:
#建立私有靜態欄位
__age = 18
def __init__(self,name):
#建立私有欄位:__name
self.__name = name
def f1(self):
print(self.__name)
# 強制訪問私有欄位,物件+一個下劃線+類名+兩個下劃線+引數
obj = foo('xiaoluo')
print(obj._foo__name)
# xiaoluo
四、類的特殊成員
常用類成員有:__doc__,__module__和__class__, __init__,__del__,__call__, __dict__,, __str__,__getitem__、__setitem__、__delitem__,__getslice__、__setslice__、__delslice__,__iter__, __new__ 和 __metaclass__,
1、__doc__:表示類的描述
class foo:
#構造方法
def __init__(self,name):
self.name= name
#輸出描述資訊
obj= foo('lcj')
print(obj.name) #呼叫物件時,系統自動執行__init__方法,輸出:lcj
print(foo.__doc__)#None
2、__module__和__class__
__module__ 表示當前操作的物件在那個模組
__class__ 表示當前操作的物件的類是什麼
class lcj:
def __init__(self):
self.name ='xiaoluo'
from lib.s4 import lcj #匯入模組
obj =lcj() #建立物件
print(obj.__module__) #輸出lib.s4模組
print(obj.__class__) # 輸出類<class 'lib.s4.lcj'>
練習:
class ww:
#建立構造方法
def __init__(self,name,age):
self.name = name
self.age = age
#析構方法:當物件結束對方法呼叫時,系統自動執行析構方法
def __del__(self):
pass
#call方法:
def __call__(self, *args, **kwargs):
print("call")
#將物件轉化至字串型別,並按照字串進行輸出
def __str__(self):
return ("%s ---%d"%(self.name,self.age))
#執行call方法:物件+()
obj1 = ww('lcj',18)
# obj1() #call
ww()()
# obj2 = ww('lcc',18)
# print(obj1)
# print(obj2)
3、 __init__
構造方法:通過類建立物件時,系統觸發執行__init__方法
class lcj:
#建立構造方法
def __init__(self,name,gender,age,):
self.name = name
self.gender = gender
self.age = age
def f1(self):
print("i am is %s,性別:%s,年齡:%s"%(self.name,self.gender,self.age))
#建立物件obj,self=obj,並將引數傳遞給__init__方法
obj = lcj("xiaoluo",18,"男")
obj.f1()
# i am is xiaoluo,性別:18,年齡:男
4、__del__
析構方法,當物件在記憶體中釋放時,系統自動觸發執行(釋放記憶體空間,充當垃圾回收)
注:此方法一般無須定義,因為Python是一門高階語言,程式設計師在使用時無需關心記憶體的分配和釋放,因為此工作都是交給Python直譯器來執行,所以,解構函式的呼叫是由直譯器在進行垃圾回收時自動觸發執行的。
class lcj:
#建立構造方法
def __init__(self,name,gender,age,):
self.name = name
self.gender = gender
self.age = age
def f1(self):
print("i am is %s,性別:%s,年齡:%s"%(self.name,self.gender,self.age))
#析構方法,
def __del__(self):
pass
#建立物件obj,self=obj,並將引數傳遞給__init__方法
obj = lcj("xiaoluo",18,"男")
obj.f1()
# i am is xiaoluo,性別:18,年齡:男
5、__call__
呼叫方式:物件+(),即觸發執行__call__方法
注:構造方法的執行是由建立物件觸發的,即:物件 = 類名() ;而對於 __call__ 方法的執行是由物件後加括號觸發的,即:物件() 或者 類()()
class lcj:
#建立構造方法
def __init__(self,name,gender,age,):
self.name = name
self.gender = gender
self.age = age
def f1(self):
print("i am is %s,性別:%s,年齡:%s"%(self.name,self.gender,self.age))
#析構方法,
def __del__(self):
pass
#建立__call__方法
def __call__(self, *args, **kwargs):
print(self.name,self.gender,self.age)
#建立物件obj,self=obj,並將引數傳遞給__init__方法
obj = lcj("xiaoluo",18,"男")
# obj.f1() #執行__init__方法
obj() #物件+():執行__call__方法
# xiaoluo 18 男
6、__dict__
獲取類和物件中所有成員
類的普通欄位屬於物件,類的靜態欄位和方法等屬於類
class lcj:
#建立靜態欄位
think = "思考中的人"
#建立構造方法
def __init__(self,name,age):
self.name = name
self.age = age
def f1(self,*args,**kwargs):
pass
#獲取類中成員,即:靜態欄位、方法
print(lcj.__dict__)
#輸出:{'f1': <function lcj.f1 at 0x000000000110CEA0>, '__module__': '__main__', '__init__': <function lcj.__init__ at 0x000000000110CE18>, '__dict__': <attribute '__dict__' of 'lcj' objects>, '__weakref__': <attribute '__weakref__' of 'lcj' objects>, 'think': '思考中的人', '__doc__': None}
#建立物件
obj = lcj("xiaoluo",18)
#獲取物件中的各成員
print(obj.__dict__)
#輸出:{'name': 'xiaoluo', 'age': 18}
__add__(self, other)方法:
class lcj:
def __init__(self,name,age):
self.name = name
self.age = age
def f1(self):
print("i am is %s age%s"%(self.name,self.age))
#__add__方法
def __add__(self, other):
tem = "%s ---%d"%(self.name,other.age)
return tem
obj1 = lcj("xiaoluo",19)
obj2 = lcj("haha",18)
ret = obj1 + obj2 #兩物件相加
print(ret)
#輸出:xiaoluo ---18
7、__str__
如果一個類中定義了__str__方法,那在列印“物件”時,預設輸出該方法的返回值
class xiaoluo:
#呼叫物件時,預設輸出__str_方法返回值
def __str__(self):
return ("haha")
obj = xiaoluo()
print(obj)
#輸出:haha
8、__getitem__、__setitem__、__delitem__
用於索引操作又可做切片方式操作,如字典,以上分別獲取,設定,刪除資料
class lcj:
#構造方法
def __init__(self,like,yanse):
self.like = like
self.yanse = yanse
#獲取
def __getitem__(self, key):
print(self.__getitem__,key)
#修改key和value
def __setitem__(self, key, value):
print(self.__setattr__,key,value)
#刪除key
def __delitem__(self, key):
print(self.__delitem__,key)
obj = lcj("藍天","藍色的天空")
obj['xiaoluo'] #系統自動觸發__getitem__方法
#輸出:<bound method lcj.__getitem__ of <__main__.lcj object at 0x0000000000B4A8D0>> xiaoluo
obj['k2'] = '中國夢'
#輸出:<method-wrapper '__setattr__' of lcj object at 0x0000000001132438> k2 中國夢
del obj['lcj'] #自動觸發:__delitem__方法
#輸出:<bound method lcj.__delitem__ of <__main__.lcj object at 0x00000000011724A8>> lcj
切片方式:
class lcj:
#構造方法
def __init__(self,like,yanse):
self.like = like
self.yanse = yanse
#獲取
def __getitem__(self, key):
print(self.__getitem__,key)
print(key.start)
print(key.stop)
print(key.step)
#修改key和value
def __setitem__(self, key, value):
# print(self.__setattr__,key,value)
print(key.start)
print(key.stop)
print(key.step)
#刪除key
def __delitem__(self, key):
print(self.__delitem__,key)
obj = lcj("藍天","藍色的天空")
# ret = obj[1:4:2] #切片呼叫:__getitem__方法,並分別打印出:起始位置:1、終止位置:4及字長:2
obj[1:4] = [1,2,3,45,5] #呼叫__setitem__方法:並分別打印出:起始位置:1、終止位置:4及字長:None
del obj[1:4]
9、__getslice__、__setslice__、__delslice__
該三個方法用於分片操作,如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*- 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__
10、__iter__
用於迭代器,之所以列表、字典、元組可以進行for迴圈,是因為型別內部定義了 __iter__
k = iter([11,23,4,5,6,7,8,])
for i in k: #迴圈打印出k列表中的各值
print(i)
11、__new__ 和 __metaclass__
python中一切都是物件
如果按照一切事物都是物件的理論:obj物件是通過執行Foo類的構造方法建立,那麼Foo類物件應該也是通過執行某個類的 構造方法 建立。
所以,kw物件是foo類的一個例項,foo類物件是 type 類的一個例項,即:foo類物件 是通過type類的構造方法建立。
class foo:
def __init__(self,name,age):
self.name = name
self.age = age
def lcj(self,):
print("--%s----%d"%(self.name,self.age))
kw = foo('jack',18) #kw是通過foo類例項化的物件
kw.lcj()
print(type(kw)) #輸出:<class '__main__.foo'> #表示kw隊形是由foo類建立
print(type(foo)) #<class 'type'> #表示foo類物件是由type類建立
建立類兩種方式
a)普通方式:
class foo(object):
def lcj(self):
print ("nihao,lcj")
f1 = foo()
f1.lcj()
b)特殊方式(type類建構函式)
def lcj():
print('hello xiaoluo')
foo = type('foo',(object,),{'lcj':lcj})
#type第一個引數:類名
#type第二個引數:當前類的基類 object
#type第三個引數:類的成員 'lcj':lcj
=====》類是由type類例項化產生
那麼問題來了,類預設是由 type 類例項化產生,type類中如何實現的建立類?類又是如何建立物件?
答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來例項化建立,所以,我們可以為 __metaclass__ 設定一個type類的派生類,從而檢視 類 建立的過程。
class MyType(type): def __init__(self, what, bases=None, dict=None):
super(MyType, self).__init__(what, bases, dict) def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs) self.__init__(obj) class Foo(object): __metaclass__ = MyType def __init__(self, name):
self.name = name def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs) # 第一階段:直譯器從上到下執行程式碼建立Foo類
# 第二階段:通過Foo類建立obj物件
obj = Foo()
五、其他相關
1、isinstance(obj,lcj)
檢查obj是否是類lcj物件
class Foo(object):
pass
obj = Foo()
ret = isinstance(obj,Foo)
print(ret) #如果obj是Foo類的物件就輸出True
# 輸出:True
2、issubclass(sub, super)
檢查sub類是否是super類的派生類(即子類)
class Foo(object):
pass
class lcj(Foo): #lcj子類繼承父類Foo
pass
ret = issubclass(lcj,Foo) #如果子類lcj是父類Foo派生類,則返回True,否則False
print(ret)
3、主動執行父類中的方法,運用super方法
class lcj: #父類
def f1(self):
print('clj.f1')
class xiaoluo(lcj): #子類繼承父類lcj
def f2(self):
#主動執行lcj類(父類)中的f1方法
super(xiaoluo,self).f1()
print('xiaoluo.f2')
obj = xiaoluo()
obj.f2()
#輸出:clj.f1
# 輸出:xiaoluo.f2
方式二:類+方法名+self關鍵字 ----》執行父類中的指定的方法
class lcj: #父類
def f1(self):
print('clj.f1')
class xiaoluo(lcj): #子類繼承父類lcj
def f2(self):
#主動執行lcj類(父類)中的f1方法
#方式二:類+方法名+self關鍵字
lcj.f1(self)
obj = xiaoluo()
obj.f2()
#輸出:clj.f1
六、異常處理