面向對象編程
面向對象編程——Object Oriented Programming,簡稱OOP,是一種程序設計思想。OOP把對象作為程序的基本單元,一個對象包含了數據和操作數據的函數。所以面向對象編程首先考慮的是新建類創建一個對象!
- 面向對象編程的三大基本特性: 封裝,繼承,多態。
- 多態指的是:子類既是自身類的實例也是父類的實例,因此任何父類實例可以使用的方法,其所有的子類實例都可以使用,不需要任何修改,這就是多態。實際上也是著名的 ‘開閉’原則 : 對擴展開放(允許增加類的子類),對修改封閉(不需要修改依賴父類型的方法,即所有的父類型包括其子類型都可以調用)。
- 通過內置函數
isinstance(obj, class_or_tuple, /)
,判斷對象是否是類或者其子類的實例
python中類的屬性和實例屬性
python沒有new關鍵字 ,python直接通過類名ClassName(),進行初始化類的實例對象。
-
類屬性:類對象(即類名)所擁有的屬性,它被該類的所有實例對象所共有,可以通過類名和實例對象訪問類屬性,在內存中只存在一個副本。類似于Java,C++中類的靜態成員變量。
-
公有的類屬性
,在類外可以通過類對象和實例對象訪問。 -
私有的類屬性__privateattr
, 以雙下劃線開頭的變量,只允許在類本身中通過類對象和實例對象進行訪問,不允許在類外部進行訪問。 -
保護的類屬性_protectedattr
,以單下劃線開頭的變量,只允許其本身和其子類中通過類對象和實例對象訪問,不能from module import *
-
修改類屬性:必須使用類對象調用,通過類對象修改類屬性后該類屬性仍然被所有實例對象所共有,如果使用實例變量修改類屬性那么該實例對象會產生一個同名的實例屬性。
實例屬性:屬于實例對象本身,類對象無法訪問,在整個類中可以調用,在類外部不可調用。實例屬性的優先級高于同名類屬性,應盡量避免同名的實例屬性和類屬性。
class B(object):
num = 0 # 公有的類屬性
__age = 24 # 私有的類屬性
_sex = 'female' # 保護的類屬性
def get_age(self):
print(self.__age) # 或者 B.__age也可以,通過類名或者實例對象self調用類屬性
def set_name(self, name):
self.name = name # 實例對象的實例屬性
def get_name(self):
return self.name # 類中調用實例屬性
b1 ,b2 = B(), B()
print(B.num) # 類名訪問類屬性,輸出: 0
print(b1.bum) # 實例對象訪問類屬性,輸出:0
print(b1.__age) # 報錯,私有的類屬性只允許在類里面訪問
b1.num = 1 # 通過實例對象修改類屬性,該實例對象會產生一個同名的實例屬性。對該實例對象來說,優先級高于同名類屬性
print(b1.num) # b1的實例屬性num,輸出:1
print(b2.num) # 輸出:0
print(B.num) # 類屬性不變,輸出:0
B.num = 2 # 通過類對象修改類屬性
print(b1.num) # 輸出:1
print(b2.num) # 輸出:2
b1.num = 1
del(b1.num) # 刪除實例對象的實例屬性
print(b1.num) # b1實例上的num屬性已被刪除所以會去類里面找同名的類屬性,輸出:0
python中類方法和實例方法和靜態方法
-
類方法
:通過內置函數classmethod()
或者修飾器@classmethod
定義,定義時必須指定第一個參數,通常用cls
。類方法可以通過類對象和實例對象訪問,調用類方法時不需要指定第一個參數cls。 -
實例方法
:定義時必須指定第一個參數,通常是self
,self代表的是調用該方法的實例對象。實例方法只能通過實例對象訪問,調用實例方法時不需要傳遞第一個參數self。 -
靜態方法
:通過內置函數staticmethod()
或者修飾器@staticmethod
定義,不需指定第一個默認參數。靜態方法可以通過類對象和實例對象訪問。
- 公有的類方法/實例方法/靜態方法
method()
: 可以在任何地方調用。 - 私有的類方法/實例方法/靜態方法
__privatemethod()
: 只允許在類內部調用。 - 保護的類方法/實例方法/靜態方法
_protectedmethod()
: 只能在當前類和其子類中調用。 -
類的專有方法
__method__()
,以雙下劃線開頭并結尾的方法,一般是系統定義的是特殊方法,例如構造函數__init__()
,析構函數__del__()
,調用方式:ClassName.__init__()
class A(object):
def __init__(self, arg): # 類的構造函數__init__()
self.arg = arg
def test_1(self):
print('instance method test_1')
@classmethod
def test_2(cls):
print('class method test_2')
print(cls.__name__) # 調用此類方法的類名
def test_3(cls):
print('method test_3')
test_3 = classmethod(test_3) # 將方法變為類方法,效果同@classmethod
@staticmethod
def test_4():
print('static method test_4')
def _test_5(self):
print('protect instance method _test_5')
def __test_6(self):
print('private instance method __test_6')
a = A() # 初始化類的實例對象, <__main__.A instance at 0x0000000004C63A88>,0x0000000004C63A88表示實實在在的內存地址
# 調用實例方法:公有的,保護的,私有的
a.test_1() # 實例對象調用方法時會將自己作為第一個參數傳遞給方法
a._test_5()
a.__test_6() # 報錯,內外面調用類的私有實例方法
# 調用類方法: 公有的,保護的,私有的
A.test_2()
a.test_2()
# 調用靜態方法: 公有的,保護的,私有的
a.test_4()
A.test_4()
- 實例對象調用方法時默認會將自己作為第一個參數傳遞給方法,類對象調用方法時默認不會傳遞任何參數
class A():
def test1():
print("test1")
def test2(name):
print(name)
a = A()
a.test1() # 報錯,TypeError: test1() takes 0 positional arguments but 1 was given,實例對象調用方法時會將自身a作為參數傳遞給test1()
A.test1() # 輸出:test1
a.test2() # 輸出:<__main__.A object at 0x0000021F953F7F60>,即輸出對象a
A.test2() # 報錯:TypeError: test2() missing 1 required positional argument: 'name'
A.test2("測試") # 輸出:測試
繼承
- python2中定義類時最好繼承內置的基類object,python3中已經默認加上即使沒有顯示聲明。
- python支持多繼承,寫法:class A(B,C,..):
- 繼承的基類從左到右優先級逐漸降低,即查找方法時從左到右在基類中查找
- 子類實例可以直接調用父類的公有的和保護的實例方法,公有的和保護的靜態方法
- 子類可以直接調用父類的公有的和保護的類方法
-
方法重寫override
,子類重寫父類的方法
class A:
def test(self):
print('A')
class B:
def test(self)
print('B')
class C(B, A):
def testC(self):
print('C')
c = C()
c.test() # 根據對象方法查找路勁從左到右在基類中查找。返回:'B'