前言
在上一篇《Python面向對象程式設計-初級篇》中,主要介紹了面向對象相關概念、面向對象相關術語、擷取或添加對象屬性、魔法方法以及Python的内置屬性,本篇内容則繼續介紹面向對象進階部分的内容:
- 面向對象的三大特性:封裝、繼承、多态
- 類中的三類變量:類變量、成員變量、局部變量
- 類中的私有方法和私有屬性
- 類的三類方法:執行個體方法、類方法、靜态方法
一、面向對象的三大特性:封裝、繼承、多态
1.封裝
封裝就是把内容封裝到某個地方,後面再從某處調用被封裝的内容
函數式程式設計的封裝
def work(name, age, work):
print(f"我叫{name},我今年{age}歲了,我的工作是{work}")
def interest(name, age, interest):
print(f"我叫{name},我今年{age}歲了,我的愛好是{interest}")
def city(name, age, city):
print(f"我叫{name},我今年{age}歲了,我的家鄉是{city}")
work('小明', 28, '司機')
interest('小明', 28, '滑雪')
city('小明', 28, '北京')
面向對象程式設計的封裝
class Introduction(object):
def __init__(self, name, age, city, work, interest)
self.name = name
self.age = age
self.city = city
self.work = work
self.interest = interest
def intro(self):
print(f"我叫{self.name},我今年{self.age}歲了,我的家鄉是{self.city},工作是{self.work},愛好是{self.interest}")
intro1 = Introduction("小明", 28, "北京", "司機", "滑雪")
intro1.intro()
intro2 = Introduction("小華", 22, "上海", "學生", "籃球")
intro2.intro()
上述對比可以看出,如果使用函數式程式設計,需要在每次執行函數時傳入相同的參數,如果參數較多,則需要多次複制粘貼;而對于面向對象,隻需要在建立對象時,将所有需要的參數封裝到目前對象中,之後再次使用時,通過self間接去目前對象中取值即可。使用面向對象的思想可以更好地模拟現實生活中的事物。
2.繼承
通過繼承建立的類稱為子類或派生類,被繼承的類稱為基類、父類或超類,子類可以繼承父類的内容,調用父類中的屬性或方法。
1)子類繼承父類
如果在子類中需要父類的構造方法就需要顯式地調用父類的構造方法,或者不重寫父類的構造方法
class ParentObject(object):
def __init__(self, height):
self.name = "當當"
self.age = 5
def parent_func(self):
print("這是父類中的方法")
class ChildObject(ParentObject):
def child_func(self):
print("這是子類中的方法")
child = ChildObject()
child.parent_func() # 這是父類中的方法
child.child_func() # 這是子類中的方法
print(ChildObject.__bases__) # (<class '__main__.ParentObject'>,)
2)子類繼承父類中的構造方法
如下案例:子類ChildObject繼承了父類ParentObject,如果想要在子類的構造方法中繼承父類構造方法中的屬性,可以有以下幾種寫法:
- ParentObject.__init__(self,height='115cm')
- super().__init__(height='115cm')
- super(ChildObject, self).__init__(height='115cm')
class ParentObject(object):
def __init__(self, height):
self.name = "當當"
self.age = 5
self.height = height
def parent_func(self):
print("這是父類中的方法")
class ChildObject(ParentObject):
def __init__(self):
# ParentObject.__init__(self,height='115cm') # 子類繼承父類的構造方法,寫法一
super().__init__(height='115cm') # 子類繼承父類的構造方法,寫法二
# super(ChildObject, self).__init__('115cm') # 子類繼承父類的構造方法,寫法三
3)類的多繼承:深度優先和廣度優先
Python中一個子類可以繼承多個父類,尋找方法有兩種,分别是:深度優先(Python2)和廣度優先(Python3)
在Python2中,經典類遵循的是深度優先的原則,新式類遵循的是廣度優先的原則;而在Python3中,無論是經典類還是新式類,都遵循廣度優先
class A(object):
name = "Asia"
def __init__(self):
print("class A")
class B(A):
def __init__(self):
print("class B")
class C(A):
def __init__(self):
print("class C")
class D(B, C):
# D類繼承了B、C,會先從B類開始查找指定屬性,B不存在時再從C開始查找,C也不存在時再從B的父類查找
def __init__(self):
print("class D")
obj = D()
print(obj.name) # Asia
由于D類繼承了B、C,會先從B類開始查找name屬性,B不存在,是以會再從C開始查找,由于C也不存在,是以會再從B的父類開始查找,最後在B的父類A中找到了name屬性,列印結果為Asia。
4)子類重寫父類方法
在子類中,使用與父類中相同的變量名或方法名,或重寫父類的屬性或方法
class Parent:
def __init__(self):
self.name = 'Lucy'
def fun_a(self):
print("this is a function in class Parent")
class Son(Parent):
def __init__(self):
super().__init__()
self.name = 'Tom' # 子類重寫父類屬性
def fun_a(self): # 子類重寫父類方法
print("this is a function in class Son")
son = Son()
print(son.name) # Tom
son.fun_a() # this is a function in class Son
3.多态
不同的子類對象,調用相同的父類方法,産生不同的結果,一種事物的多種展現形式,函數的重寫其實就是多态的一種展現
class Animals(object):
def talk(self):
print("animals")
class Person(Animals):
def talk(self):
print("person")
class Cat(Animals):
def talk(self):
print("cat")
class Dog(Animals):
def talk(self):
print("dog")
Person().talk() # person
Cat().talk() # cat
Dog().talk() # dog
如上圖所示,Person、Dog、Cat分别繼承了Animals類,但是分别重寫了talk方法,當這三個類分别被調用時會執行自己類中所定義的talk方法,而非父類Animals中的talk方法
二、類變量、成員變量、局部變量
1.類變量
類變量在整個執行個體化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作為執行個體變量使用。可以由類名直接調用,也可由對象來調用。
# 類變量
class A:
name = 'Tony'
def fun_a(self):
print('this is a test function in class A')
print(A.name) # Tony
print(A().name) # Tony
2.執行個體變量(成員變量)
在類的聲明中,屬性是用變量來表示的。這種變量就稱為執行個體變量,是在類聲明的内部但是在類的其他成員方法之外聲明的,在構造方法中以self. 開頭來定義。執行個體變量隻能通過對象來調用,不能通過類名調用。
# 執行個體變量(成員變量)
class B:
def __init__(self):
self.city = 'suzhou' # 執行個體變量
self.street = '松濤街' # 執行個體變量
# 在構造方法中提前聲明了一個方法,這個方法中所包含的變量也屬于成員變量
self.vars()
def vars(self):
self.home = "月亮灣壹号"
self.house = "1幢一單元108"
print(B().city) # suzhou
print(B().street) # 松濤街
print(B().__dict__) # {'city': 'suzhou', 'street': '松濤街', 'home': '月亮灣壹号', 'house': '1幢一單元108'}
3.局部變量
定義在方法中的變量,隻作用于目前執行個體的類。如以下方法中的mobile就屬于局部變量。
def info(self):
self.number = 227 # 局部變量
self.phone = 15252162666 # 局部變量
三、類中私有方法和私有屬性
1.類的私有屬性
__private_attrs:兩個下劃線開頭,聲明該屬性為私有,不能在類的外部被使用或直接通路。在類内部的方法中使用時 self.__private_attrs
# 私有屬性
class ParentObject(object):
mobile = 15252162666 # 類變量
__private_mobile = 15252162666 # 私有變量
def fun(self):
print("列印私有變量{}".format(self.__private_mobile))
def func(self):
return self.__fun()
po = ParentObject()
po.fun() # 列印私有變量15252162666
2.類的私有方法
__private_method:兩個下劃線開頭,聲明該方法為私有方法,不能在類的外部調用。在類的内部調用 self.__private_methods
def __fun(self):
print("這是一個私有化方法")
def func(self):
return self.__fun()
3.外部調用類中的私有屬性或方法
通常私有屬性和私有方法隻能在類的内部被調用,外部是不可以調用的。但如果強行調用,也是可以的,相當于Python中開了個後門:
外部調用類的私有屬性:對象名._類名__屬性名
外部調用類的私有方法:對象名._類名__方法名
# 外部調用類的私有屬性:對象名._類名__屬性名
print(po._ParentObject__private_mobile)
# 外部調用類的私有方法:對象名._類名__方法名
po._ParentObject__fun()
四、類的三類方法:執行個體方法、類方法、靜态方法
1.執行個體方法
第一個參數必須是執行個體對象,該參數一般約定為self,通過它來傳遞執行個體的屬性和方法(也可以傳類的屬性和方法),隻能由執行個體對象調用
# 執行個體方法
class Example:
def fun_a(self):
print("這是一個執行個體方法")
# 調用執行個體方法,隻能由執行個體對象調用
ex = Example()
ex.fun_a() # 這是一個執行個體方法
2.類方法
使用裝飾器@classmethod,第一個參數必須是目前類對象,該參數名一般約定為“cls”,通過它來傳遞類的屬性和方法(不能傳執行個體的屬性和方法),類和執行個體對象都可以調用
@classmethod
def fun_b(self):
print("這是一個類方法")
# 調用類方法,執行個體對象和類名都可以調用,使用類名直接調用時,不會執行類中的構造方法
ex.fun_b() # 這是一個類方法
Example.fun_b() # 這是一個類方法
注:使用類名直接調用時,不會執行類中的構造方法
3.靜态方法
使用裝飾器@staticmethod修飾,參數随意,沒有“self”和“cls”參數,但是方法體中不能使用類或執行個體的任何屬性和方法,一般用于和類本身無太多關聯但又會綁定在類中的場景(不可使用類中的屬性和方法),如擷取時間等等。類和執行個體對象都可以調用
@staticmethod
def fun_c():
print("這是一個靜态方法")
# 調用靜态方法,執行個體對象和類名都可以調用,不能使用類或執行個體的任何屬性和方法
ex.fun_c() # 這是一個靜态方法
Example.fun_c() # 這是一個靜态方法
小結
類别 | 調用方式 | 注意事項 |
類變量 | 執行個體對象和類都可以調用 | |
執行個體變量(成員變量) | 隻能通過執行個體對象調用 | |
局部變量 | 隻能在方法内部調用 | |
類的私有屬性:__private_attrs | 隻能在類的内部調用:self.__private_attrs | 外部強行調用類的私有屬性:對象名._類名__屬性名 |
類的私有方法:__private_method | 隻能在類的内部調用:self.__private_methods | 外部強行調用類的私有方法:對象名._類名__方法名 |
執行個體方法 | 隻能由執行個體對象調用 | |
靜态方法:@staticmethod | 執行個體對象和類都可以調用 | |
類方法:@classmethod | 執行個體對象和類都可以調用 | 使用類名調用時,不會執行類中的構造方法 |