前言零、面向?qū)ο蟮娜筇卣?/h2>封裝、繼承和多態(tài)是面向?qū)ο蟮娜筇卣鳌?/p> 封裝是為了提高程序的安全性; 繼承是為了提高代碼的復(fù)用性,; 多態(tài)是為了提高程序的可擴(kuò)展性和可維護(hù)性。 值得一提的是,,這三大特征與語言本身無關(guān),,這是一種面向?qū)ο蟮木幊趟枷搿?/p>
一、封裝什么是封裝,? 其實(shí)封裝就是包裝,,例如銀行里的ATM機(jī)里放著錢,機(jī)器包裝著錢,,而我們根本不知道錢在機(jī)器的哪個位置放著,我們只能通過合適的方式去取錢,。這樣就提高了錢的安全性,。
寫程序時的封裝也是一樣的,我們將數(shù)據(jù)(屬性)和行為(方法)包裝到類對象中,。而在方法內(nèi)部對屬性進(jìn)行操作,,在類對象的外部調(diào)用方法。這樣我們只需要用合適的方式去用它,,而不用關(guān)心剛方法時如何具體實(shí)現(xiàn)的,。
在Python中沒有專門的修飾符用于屬性的私有,如果不希望該屬性在類對象外部被訪問,,可在前邊使用兩個'_'
class Student:def __init__(self,name,age):self.name=nameself.__age=age #前邊加兩_表示不希望再類外部被訪問def show(self):print(self.name,self.__age)stu=Student('蘇沐',20)stu.show()print(stu.name)#print(stu.__age) #報錯print(dir(stu)) #可查看類內(nèi)的屬性和方法print(stu._Student__age) #在類的外部可通過_Student__age訪問
注意:如果一定要在類外部訪問,,可通過_Student__age訪問,但大家都比較自覺,,人家都不讓你問訪問了,,你就別這么訪問了
二、繼承1.繼承 簡單來說繼承就是定義子類,,子類繼承父類的屬性和方法,。 語法: class 子類類名(父類1,,父類2…): pass
如果一個類沒有繼承任何類,默認(rèn)繼承object,; Python支持多繼承,; 定義子類時,必須在其構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù),。
#定義父類class Person(object): #object可寫可不寫def __init__(self,name,age):self.name=nameself.__age=age #前邊加兩_表示不希望在類外部被訪問def show(self):print(self.name,self.__age)#定義子類class Student(Person):def __init__(self,name,age,score):super().__init__(name,age) #調(diào)用父類的構(gòu)造函數(shù)self.score=score #自己額外的屬性#調(diào)用stu=Student('蘇沐',20,99)stu.show()
2.方法重寫 上述代碼中由于我們繼承了Person類,,調(diào)用show時只輸出name和age,我們需要它還輸出score就需要方法重寫,。 如果子類對繼承自父類的某個屬性或方法不滿意,,可以在子類中對其方法進(jìn)行重新編寫; 子類重寫后的方法中,,可以通過super().xxx()調(diào)用父類中被寫過的方法,。
#定義父類class Person(object): #object可寫可不寫def __init__(self,name,age):self.name=nameself.__age=age #前邊加兩_表示不希望在類外部被訪問def show(self):print(self.name,self.__age)#定義子類class Student(Person):def __init__(self,name,age,score):super().__init__(name,age) #調(diào)用父類的構(gòu)造函數(shù)self.score=score #自己額外的屬性def show(self): #方法重寫super().show()print(self.score)#調(diào)用stu=Student('蘇沐',20,99)stu.show()
3.object類 object類是所有類的父類,所以所有類內(nèi)都有object類的屬性和方法 內(nèi)置函數(shù)dir()可查看指定對象的所有屬性 object有一個__str__()方法,,用于返回一個對于”對象的描述”,,對應(yīng)于內(nèi)置函數(shù)str(),經(jīng)常用于print()方法,,幫我們查看對象信息,,所以我們經(jīng)常會對__str__進(jìn)行重寫。
class Student:def __init__(self,name,age):self.name=nameself.age=agedef __str__(self):return '我的名字是{0},,今年{1}歲'.format(self.name,self.age)stu=Student('蘇沐',20)print(dir(stu)) #查看對象信息print(str(stu)) #沒重寫__str__前輸出對象地址,,重寫后輸出你重寫返回的內(nèi)容print(stu) #和上面一句等價,直接輸出的話默認(rèn)調(diào)用__str__()方法print(type(stu))
三,、多態(tài) 很多人說,,Python中沒有多態(tài)的概念,因為Python中的變量是沒有數(shù)據(jù)類型的,。 其實(shí)不然,,雖然Python中變量沒有數(shù)據(jù)類型,但是仍然具備多態(tài)的特征,。
Python中的多態(tài)指的是,,在運(yùn)行過程中根據(jù)變量所引用對象的類型,動態(tài)的決定調(diào)用哪個類對象中的方法,。
class Animal(object):def run(self):print('動物會跑')class Dog(Animal):def run(self):print('狗會跑')class Cat(Animal):def run(self):print('貓會跑')class Person():def run(self):print('人會跑')#定義一個函數(shù)def fun(obj):obj.run()#調(diào)用函數(shù)fun(Animal())fun(Cat())fun(Dog())fun(Person())
這里Animal,、Cat、Dog,、Person都調(diào)用了run方法,,Cat和Dog是對Animal中run的重寫,很容易理解,但是Person中的run與其他三個中的是并列的,,調(diào)用的時候就看傳入對象的類型屬于哪個就調(diào)用哪個,。這是與其他編程語言所不同的,我們稱為“鴨子類型”,。
Python是一種動態(tài)的語言,。 靜態(tài)語言與動態(tài)語言關(guān)于多態(tài)的區(qū)別: 靜態(tài)語言實(shí)現(xiàn)多態(tài)的三個必要條件: 繼承、方法重寫,、父類引用指向子類對象 動態(tài)語言的多態(tài)稱為“鴨子類型”,,當(dāng)一只鳥走路,游泳,,啄食等行為都像鴨子的時候,,我們認(rèn)為它就是鴨子。我們不關(guān)心對象是什么類型,,只關(guān)心對象的行為(方法),。
四、特殊方法和特殊屬性(***)以兩個下劃線開始和結(jié)束的如__xxx__稱為特殊屬性和特殊方法,。 1.特殊屬性 #特殊屬性__dict__獲得類對象或?qū)嵗瘜ο笏壎ǖ乃袑傩院头椒ǖ淖值?/p> #dir(object)可查看object中的屬性,、方法class A:passclass B:passclass C(A,B):def __init__(self,name,age):self.name=nameself.age=age#創(chuàng)建C類對象c=C('蘇沐',20)#查看實(shí)例對象的屬性字典,方法是在類對象中的,,實(shí)例對象只能調(diào)用print(c.__dict__)# {'name': '蘇沐', 'age': 20}#查看類對象的屬性和方法字典print(C.__dict__) #{'__module__': '__main__', '__init__': <function C.__init__ at 0x000001CB9F854D30>, '__doc__': None}print(c.__class__) #輸出對象所屬的類 <class '__main__.C'>print(C.__bases__) #輸出C類的父類元組 (<class '__main__.A'>, <class '__main__.B'>)print(C.__base__) #輸出類的第一個父類 <class '__main__.A'>print(C.__mro__) #輸出類的層次結(jié)構(gòu)print(A.__subclasses__()) #輸出A的子類
2.特殊方法 #特殊方法# __add__ 通過重寫__add__方法,,可使自定義對象具有+功能# 通過重寫__len__方法,讓內(nèi)置函數(shù)len()的參數(shù)可以是自定義類型a=10b=20c=a+b #實(shí)際過程為下一行代碼d=a.__add__(b)print(c,d)class Student:def __init__(self,name):self.name=namedef __add__(self,other):return self.name+other.namedef __len__(self):return len(self.name)stu1=Student('蘇沐')stu2=Student('禿頭')stu=stu1+stu2 #重定義__add__后就不會報錯了,,因為在Student中編寫了特殊方法stu=stu1.__add__(stu2)print(stu)print(len(stu))
#__new__()用于創(chuàng)建對象#__init__()對創(chuàng)建的對象進(jìn)行初始化class Person(object):def __new__(cls,*args,**kwargs):print('__new__被調(diào)用了,,cls的id值為{0}'.format(id(cls)))obj=super().__new__(cls)print('創(chuàng)建的對象的id為:{0}'.format(id(obj)))return objdef __init__(self,name,age):print('__init__被調(diào)用了,self的id值為:{0}'.format(id(self)))self.name=nameself.age=ageprint('object這個類對象的id為:{0}'.format(id(object)))print('Person這個類對象的id為:{0}'.format(id(Person)))#創(chuàng)建實(shí)例對象p1=Person('蘇沐',20)print('p1這個Person類的實(shí)例對象的id為{0}:'.format(id(p1)))
執(zhí)行過程為:在創(chuàng)建實(shí)例對象的過程中將Student傳給cls,然后object中創(chuàng)建一個對象obj,,傳給init方法中的self初始化,,完成后賦值給p1.所以后輸出的三個的id地址都是一樣的。 總的來說,,我們想讓該方法實(shí)現(xiàn)哪些內(nèi)容,該方法的方法體就由我們來寫,。許多的特殊方法都對應(yīng)于一些內(nèi)置函數(shù),。
五、類的賦值與拷貝(***)1.類的賦值 只是形成了兩個變量,,實(shí)際上還是指向同一個實(shí)例對象,。 class A():passa1=A()a2=a1print(id(a1)) #2364867875104print(id(a2)) #2364867875104
2.類的淺拷貝 Python拷貝一般都是淺拷貝,拷貝時,,對象包含的子對象內(nèi)容不拷貝,,因此,源對象與拷貝對象會引用同一個子對象 import copya2=copy.copy(a1)
3.類的深拷貝 使用copy模塊的deepcopy函數(shù),遞歸拷貝對象中包含的子對象,,源對象和拷貝對象所有的子對象也不相同,。 #深拷貝import copya3=copy.deepcopy(a1)
總結(jié)
|