今天要學(xué)習(xí)的是面向?qū)ο笈哐欤琍ython從設(shè)計之初就已經(jīng)是一門面向?qū)ο蟮恼Z言旁涤,正因為如此碉咆,在Python中創(chuàng)建一個類和對象是很容易的,我們先一起來看一下類和對象的定義~
類和對象的定義
class 類的名稱:
語句塊
# 舉例說明
class Student:
num = 100
def showNum(self):
return 200
print(Student.num) # 輸出:100
print(Student.showNum)
# 輸出:<function Student.showNum at 0x009A9468>
# Student就是類對象败玉,num是類變量敌土,showNum是方法镜硕,self為類對象的實(shí)例, 類名稱一般需要大寫
類:用來描述具有相同的屬性和方法的對象的集合返干;
對象:通過類定義的數(shù)據(jù)結(jié)構(gòu)實(shí)例谦疾;
簡單來說就是在python中,用屬性表示特征犬金,用方法表示技能念恍,因而具有相同特征和技能的一類事物就是‘類’,對象是則是這一類事物中具體的一個晚顷;
實(shí)例的定義
講完類和對象峰伙,我們來看一下實(shí)例,類是抽象的模板该默,而實(shí)例是根據(jù)類創(chuàng)建出來的一個個具體的“對象”瞳氓,每個對象都擁有相同的方法,但各自的屬性可能不同栓袖;
class Student:
def prt(self):
print(self)
print(self.__class__)
s = Student()
s.prt()
# 執(zhí)行結(jié)果:
# <__main__.Student object at 0x0000000001D7CDA0>
# <class '__main__.Student'>
上述例子中self
代表的是類的實(shí)例匣摘,而self.__class__
指向類,且self 不是 python 關(guān)鍵字裹刮,我們把self換成其它的單詞也可以正常運(yùn)行音榜;
什么是實(shí)例化
我們知道了什么是實(shí)例,那實(shí)例化又是什么呢捧弃,接著看下面這個例子赠叼,在Student類中有兩個方法,一個是初始化方法__init__
违霞,另一個是我自己定義的方法showClass()方法
class Student:
num = 100
def __init__(self):
self.name = '張三'
def show(self):
return '李四'
print(Student.num) # 輸出:100
print(Student.show)
輸出:<function Student.show at 0x0000000002765BF8>
stu = Student()
# 這就是實(shí)例化嘴办,且實(shí)例化會自定調(diào)用__init__方法,self會自動傳遞买鸽,不能有return返回值
print(stu.name) # 張三
類名加括號就是實(shí)例化涧郊,實(shí)例化會自動調(diào)用
__init__()
方法,可以用它來為每個實(shí)例定制自己的特征(屬性)眼五;init()
方法被稱為類的構(gòu)造函數(shù)或初始化方法妆艘,需要注意的是__init__()
方法不能有return返回值;
類變量和實(shí)例變量
然后我們來講一下什么是類變量和實(shí)例變量,看看這兩者之間有什么不同啼肩;
# 實(shí)例變量是實(shí)例特有的,類變量是類和實(shí)例共有的
class Student:
num = 100
def __init__(self, name):
self.name = name
def showClass(self):
return 200
print(Student.num)
print(Student.showClass)
stu = Student('張三') # 實(shí)例化,會調(diào)用__init__方法劳淆,self會自動傳遞,不能有return返回值
print(stu.name) # 實(shí)例變量
print(stu.num) # 類變量
直接看這個實(shí)例可能還不太直觀,大家自己在程序里面動手運(yùn)行一下,使用Student類調(diào)用num和name兩個變量逗物,就會發(fā)現(xiàn)在調(diào)用num的時候能正常輸出,而調(diào)用name的時候報錯了瑟俭。然后再用實(shí)例對象stu調(diào)用這兩個變量試試翎卓,我們就能得出下面兩個結(jié)論:
類變量:類變量在整個實(shí)例化的對象中是公用的,也就是定義在類中且在函數(shù)體之外的變量(但類變量通常不作為實(shí)例變量使用)摆寄;
實(shí)例變量:在類的聲明中失暴,屬性是用變量來表示的,這種變量就稱為實(shí)例變量微饥,是定義在類里面的方法中的變量逗扒;
類方法和實(shí)例方法
講完了類變量和實(shí)例變量,那我們來看一下類方法和實(shí)例方法:
class Student:
num = 100
def __init__(self, name):
self.name = name
def showClass(self):
return '張三'
@classmethod
def add(cls):
print(cls)
stu = Student('張三') # 實(shí)例化欠橘,會調(diào)用__init__方法矩肩,self會自動傳遞,不能有return返回值
print(stu.name) # 實(shí)例變量 輸出:張三
print(stu.num) # 類變量 輸出: 100
print(stu.__dict__) # 類的屬性保存在自己的字典中肃续,包括類變量和方法
print(stu.__dict__) # 實(shí)例的屬性保存在自己的字典中黍檩,包括實(shí)例的變量
stu.add() # 類方法可以被實(shí)例對象調(diào)用 輸出:<class '__main__.Student'>
stu.__class__.add()
print(Student.showClass()) # 報錯,實(shí)例方法不可以被類對象調(diào)用
print(Student.name) # 實(shí)例可以訪問類的屬性始锚,類無法訪問實(shí)例的屬性
我們動手試一下刽酱,分別用Student類,和實(shí)例對象stu來調(diào)用類方法和實(shí)例方法瞧捌,我們就能得出一下結(jié)論:
類方法使用裝飾器@classmethod肛跌,第一個參數(shù)必須是當(dāng)前類對象,該參數(shù)名一般約定為“cls”察郁,通過它來傳遞類的屬性和方法(不能傳實(shí)例的屬性和方法)衍慎;
類方法可以被實(shí)例對象和類對象調(diào)用;
實(shí)例方法第一個參數(shù)必須是實(shí)例對象皮钠,該參數(shù)名一般約定為“self”稳捆,通過它來傳遞實(shí)例的屬性和方法(也可以傳類的屬性和方法);
實(shí)例方法只能由實(shí)例對象調(diào)用麦轰;
靜態(tài)方法
除了類方法和實(shí)例方法之外乔夯,還有一個方法叫做靜態(tài)方法,我們一起來看一下靜態(tài)方法如何使用:
class Student:
def __init__(self, name):
self.name = name
@staticmethod
def sub():
print('static')
stu = Student('李四')
stu.sub()
# 實(shí)例可以調(diào)用靜態(tài)方法 輸出:static
Student.sub()
# 類可以調(diào)用靜態(tài)方法 輸出:static
然后我們可以得出一下兩個結(jié)論:
靜態(tài)方法使用裝飾器@staticmethod款侵,參數(shù)隨意末荐,沒有“self”和“cls”參數(shù),但是方法體中不能使用類或?qū)嵗娜魏螌傩院头椒ǎ?/p>
靜態(tài)方法可以被實(shí)例對象和類對象調(diào)用新锈;
私有屬性和保護(hù)屬性
兩個下劃線
__
開頭的屬性為私有屬性甲脏,不能在類的外部被使用或直接訪問;一個下劃線
_
開頭的為保護(hù)屬性,只有類實(shí)例和子類實(shí)例能訪問块请,需通過類提供的接口進(jìn)行訪問娜氏;
我們來看一下私有屬性和保護(hù)屬性是如何使用的:
class Student(object):
def __init__(self):
self.__private_field = 200 # private
self._protect_field = 200 # protect
stu = Student()
# print(stu.__private_field) # 報錯,實(shí)例對象不可以調(diào)用私有屬性
print(stu._protect_field) # 輸出:200
屬性裝飾器
- 第一種寫法:使用@property裝飾器墩新,將類的方法變?yōu)閷傩裕?/li>
class Student:
num = 100
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
@name.deleter
def name(self):
del self.__name
stu = Student('張三')
print(stu.name) # 輸出:張三
stu.name = '李四'
print(stu.name) # 輸出:李四
- 第二種寫法:使用屬性函數(shù)property()贸弥,直接把方法當(dāng)屬性來操作;
class Student:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self, value):
self.__name = value
def del_name(self):
del self.__name
print('實(shí)例的屬性被刪除了')
# 這里表示property是用來修飾name這個屬性的
name = property(fget=get_name, fset=set_name, fdel=del_name, doc='hello')
stu = Student('張三')
print(stu.name) # 張三
stu.name = '李四' # 給name賦值
print(stu.name) # 李四
- 一個property對象包含三個方法:getter, setter, deleter海渊,當(dāng)一個函數(shù)被@property裝飾器修飾時绵疲,系統(tǒng)會自動創(chuàng)建一個包含對應(yīng)訪問函數(shù)的同名屬性;
類的繼承
class Animal:
def __init__(self):
self.type = 'animals'
def eat(self):
print('{} 吃臣疑,Animal類中'.format(self.__class__.__name__)) # Animal 吃最岗,Animal類中
class Person(Animal):
def talk(self):
print('講話')
# animal = Animal()
# animal.eat()
#
# print(animal.type) # animals
# animal.talk() # 報錯,父類不可以調(diào)用子類的方法
person = Person()
print(person.type) # animals
# Person調(diào)用Animal類的eat方法
person.eat() # 輸出:Person 吃朝捆,Animal類中
person.talk() # 講話
繼承是一種創(chuàng)建類的方法般渡,一個類可以繼承來自一個或多個父類,原始類稱為基類或超類芙盘;
繼承可以很方便的幫助子類擁有父類的屬性和方法驯用,減少代碼冗余,子類可以定義自己的方法和屬性儒老,也可以覆蓋父類的方法和屬性蝴乔;
實(shí)現(xiàn)繼承:指使用基類的屬性和方法而無需額外編碼的能力;
接口繼承:指僅使用屬性和方法的名稱驮樊、但是子類必須提供實(shí)現(xiàn)的能力(子類重構(gòu)父類方法)薇正;
屬性查找順序
class Animal:
__name = 'animal'
def __init__(self):
self.type = 'animal'
def eat(self):
print('{} eat'.format(self.__class__.__name__))
class Person(Animal):
def talk(self):
print('talk')
person = Person()
print(person.__name) # 子類不能訪問 父類的私有屬性
父類的私有屬性無法被子類訪問;
屬性的查找順序:先從對象自身的
__dict__
中查找->然后從對象所在類的__dict__
中查找->然后從父類的__dict__
中查找囚衔,直至找到或者報錯沒有找到挖腰;
方法重寫
- 子類可以覆蓋父類的方法,可以在覆蓋的方法中調(diào)用父類的方法练湿,且父類的類方法猴仑,靜態(tài)方法也可以被覆蓋
class Animal:
def __init__(self):
self.type = 'animal'
def eat(self):
print('{} eat'.format(self.__class__.__name__))
class Person(Animal):
def eat(self):
print('洗手') # 洗手
print('{} eat'.format(self.__class__.__name__)) # Person eat
super().eat() # 調(diào)用父類的方法 # Person eat
# super(Person, self).eat() 等價于 super().eat()
person = Person()
person.eat()
- 繼承中的init方法
class Animal:
def __init__(self):
self.one = 'one'
class Person(Animal):
def __init__(self):
self.two = 'two'
self.three = 'three'
def show(self):
print(self.one, self.two, self.three)
person = Person()
print(person.__dict__) # 輸出:{'two': 'two', 'three': 'three'}
person.show() # 報錯,person沒有one這個屬性
print(person.one) #同樣報錯
- 代碼修改肥哎,如果父類有init方法辽俗,且子類也有init方法,最好在子類的init方法中手動調(diào)用父類的init方法
class Animal:
def __init__(self):
self.one = 'one'
class Person(Animal):
def __init__(self):
self.two = 'two'
self.three = 'three'
super().__init__() # 繼承父類的__init__方法篡诽,且父類init方法的調(diào)用寫在子類的什么地方也有講究
def show(self):
print(self.one, self.two, self.three)
person = Person()
print(person.__dict__)
print(person.one)
# 輸出:one 崖飘,因為繼承了父類的init__方法,所以可以訪問one屬性
如果你的父類方法的功能不能滿足你的需求杈女,就可以在子類重寫你父類的方法朱浴;
子類可以覆蓋父類的方法吊圾,且可以在覆蓋的方法中調(diào)用父類的方法;
super()
函數(shù)是用于調(diào)用父類(超類)的一個方法赊琳, Python 3 可以使用直接使用super().xxx 代替 super(Class, self).xxx
;
裝飾器
裝飾器是一個很著名的設(shè)計模式砰碴,經(jīng)常被用于有切面需求的場景躏筏,較為經(jīng)典的有插入日志、性能測試呈枉、事務(wù)處理等趁尼;
python裝飾器本質(zhì)上就是一個函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外的功能猖辫,裝飾器的返回值也是一個函數(shù)對象(函數(shù)的指針)酥泞;
概括的講,裝飾器的作用就是為已經(jīng)存在的對象添加額外的功能啃憎,也稱之為擴(kuò)展功能芝囤;
Mixin
Mixin是一種設(shè)計模式,通過多繼承的方式對類的功能進(jìn)行增強(qiáng)辛萍;
Mixin可以在不修改任何源代碼的情況下悯姊,對已有類進(jìn)行擴(kuò)展;
可以根據(jù)需要使用已有的功能進(jìn)行組合贩毕,來實(shí)現(xiàn)“新”類悯许;
還能很好的避免類繼承的局限性,因為新的業(yè)務(wù)需要可能就需要創(chuàng)建新的子類辉阶;
Mixin類的注意點(diǎn)
在Mixin類中先壕,不能寫初始化的
__init__
方法,因為Mixin類不做為獨(dú)立類使用谆甜;Mixin類原則上必須作為其他類的基類垃僚,實(shí)現(xiàn)其他類的功能增強(qiáng);
Mixin類的基類必須也是Mixin類规辱;
使用Mixin方式增強(qiáng)功能的的類冈在,必須將Mixin類寫在繼承列表的第一個位置;
Mixin比decorator更加強(qiáng)大按摘;