一福贞、封裝【private】
1.概念
廣義的封裝:函數(shù)和類的定義本身撩嚼,就是封裝的體現(xiàn)
狹義的封裝:一個類的某些屬性,在使用的過程 中挖帘,不希望被外界直接訪問完丽,而是把這個屬性給作為私有的【只有當前類持有】,然后暴露給外界一個訪問的方法即可【間接訪問屬性】
封裝的本質(zhì):就是屬性私有化的過程
封裝的好處:提高了數(shù)據(jù)的安全性拇舀,提高了數(shù)據(jù)的復(fù)用性
說明:舉例:插排逻族,不需要關(guān)心屬性在類的內(nèi)部做了什么樣的操作,只需要關(guān)心將值傳進去骄崩,或者將結(jié)果獲取出來
2.屬性私有化
如果想讓成員變量不被外界直接訪問聘鳞,則可以在屬性名稱的前面添加兩個下劃線__,成員變量則被稱為私有成員變量
私有屬性的特點:只能在類的內(nèi)部直接被訪問,在外界不能直接訪問
代碼演示:
#1.屬性不私有化的時候 class Person(): def __init__(self,name,age): self.name = name self.age = age def myPrint(self): print(self.name,self.age) #通過構(gòu)造函數(shù)給屬性賦值 per = Person("張三",10) per.myPrint() #張三 10 #通過對象直接訪問屬性要拂,并且給屬性賦值 per.name = "李四" per.age = 22 per.myPrint() #李四 22 #2.屬性私有化 #寫法:在屬性的前面添加兩個下劃線 #用法:只能在類的內(nèi)部被訪問抠璃,外界不能直接訪問 class Person1(): def __init__(self,name,age): self.name = name self.__age = age def myPrint(self): print(self.name,self.__age) p1 = Person1("abc",10) p1.myPrint() #abc 10 p1.name = "hello" #其實動態(tài)綁定屬性,age和__age其實是兩個不同的變量 p1.age = 222 p1.myPrint() print(p1.age) #AttributeError: 'Person1' object has no attribute '__age',私有化了脱惰,在外界不能直接訪問 #print(p1.__age)
3.get函數(shù)和set函數(shù)
get函數(shù)和set函數(shù)并不是系統(tǒng)的函數(shù)搏嗡,而是自定義的,為了和封裝的概念相吻合拉一,起名為getXxx和setXxx
get函數(shù):獲取值
set函數(shù):賦值【傳值】
代碼演示:
#3.get函數(shù)和set函數(shù) class Person2(): def __init__(self,name,age): self.name = name self.__age = age #特殊情況一 self.__weight__ = 20.0 #特殊情況二 self._height = 155.0 def myPrint(self): print(self.name,self.__age) # 書寫私有屬性age的get函數(shù)和set函數(shù)【通過自定義的函數(shù)進行私有屬性的賦值和獲取值采盒,暴露給外界】 """ get函數(shù)和set函數(shù)并不是系統(tǒng)的函數(shù)旧乞,而是自定義的,為了和封裝的概念相吻合纽甘,起名為getXxx和setXxx get函數(shù):獲取值 set函數(shù):賦值【傳值】 """ #set函數(shù):給成員變量賦值 #命名方式:setXxx #特點:需要設(shè)置參數(shù)良蛮,參數(shù)和私有成員變量有關(guān) def setAge(self,age): #數(shù)據(jù)的過濾 if age < 0: age = 0 self.__age = age #get函數(shù):獲取成員變量的值 #命名方式:getXxx #特點:需要設(shè)置返回值抽碌,將成員變量的值返回 def getAge(self): return self.__age #注意:有幾個私有屬性悍赢,則書寫幾對get函數(shù)和set函數(shù) p2 = Person2("abc",10) p2.myPrint() #abc 10 #print(p2.__age) #間接的訪問了私有的成員變量 print(p2.getAge()) p2.setAge(22) print(p2.getAge()) p2.setAge(-20) print(p2.getAge()) #總結(jié):通過將屬性私有化之后,然后提供get函數(shù)和set函數(shù)货徙,外部代碼就不能隨意更改成員變量的值左权,這樣在一定程度上保證了數(shù)據(jù)的安全性 #4.工作原理【了解】 #當編譯器加載了程序之后,不能直接訪問p2.__age,Python解釋器把__age解釋成_Person2__age #p2.__age = 100 p2._Person2__age = 100 print(p2.getAge()) #5.特殊情況:盡量不要直接訪問 #a.在一個變量的前后各加兩個下劃線痴颊,在Python中被認為特殊成員變量赏迟,將不再屬于私有變量 #print(p2.__weight__) #b.特殊變量 #print(p2._height) #面試題:下面變量的含義 """ xxx:普通的變量 _xxx:受保護的變量,不建議使用這種形式 __xxx:表示私有的蠢棱,外界無法直接訪問锌杀,只能通過暴露給外界的函數(shù)訪問 __xxxx__:一般是系統(tǒng)的內(nèi)置變量,比如:__name__,__solts__,自定義標識符的時候盡量不要使用這種形式 """
4.@property裝飾器
裝飾器的作用:可以給函數(shù)動態(tài)添加功能泻仙,對于類的成員方法糕再,裝飾器一樣起作用
Python內(nèi)置的@property裝飾器的作用:將一個函數(shù)變成屬性使用
@property裝飾器:簡化get函數(shù)和set函數(shù)
使用:@property裝飾器作用相當于get函數(shù),同時玉转,會生成一個新的裝飾器@屬性名.settter,相當于set函數(shù)的作用
作用:使用在類中的成員函數(shù)中突想,可以簡化代碼,同時可以保證對參數(shù)做校驗
代碼演示:
class Person1(): def __init__(self,name,age): self.__name = name self.__age = age def myPrint(self): print(self.__name,self.__age) """ def setAge(self,age): #數(shù)據(jù)的過濾 if age < 0: age = 0 self.__age = age def getAge(self): return self.__age """ #注意:函數(shù)的命名方式:變量的名稱 #作用:相當于get函數(shù)究抓,設(shè)置返回值猾担,將成員變量的值返回 @property def age(self): return self.__age #注意:函數(shù)的命名方式:需要和@property中函數(shù)的命名保持一致 #作用:相當于set函數(shù),設(shè)置參數(shù),給成員變量賦值 @age.setter def age(self,age): if age < 0: age = 0 self.__age = age @property def name(self): return self.__name @name.setter def name(self,name): self.__name = name p1 = Person1("abc",10) p1.myPrint() #abc 10 #p1.setAge(20) #print(p1.getAge()) print(p1.age) #10 p1.age = 18 #相當于調(diào)用了set函數(shù)刺下,將18傳值绑嘹,實質(zhì)調(diào)用的是@age.setter修飾的函數(shù) print(p1.age) #相當于調(diào)用了get函數(shù),將成員變量的值獲取出來橘茉,實質(zhì)調(diào)用的是@peoperty修飾的函數(shù) p1.name = "zhangsan" print(p1.name)
5.私有方法
如果類中的一個函數(shù)名前面添加__,則認為這個成員函數(shù)時私有化的
特點:也不能在外界直接調(diào)用工腋,只能在類的內(nèi)類調(diào)用
代碼演示:
class Site(): def __init__(self,name): self.name = name def who(self): print(self.name) self.__foo() #私有成員方法,只能在當前類的內(nèi)部內(nèi)調(diào)用 def __foo(self): #私有函數(shù) print("foo") def foo(self): #公開函數(shù) print("foo~~~~") #注意:以上兩個函數(shù)是兩個不同的函數(shù)捺癞,不存在覆蓋的問題 s = Site("千鋒") s.who() #s.__foo() #AttributeError: 'Site' object has no attribute 'foo' s.foo()
二夷蚊、繼承【extends】
1.概念
如果兩個或者兩個以上的類具有相同的屬性或者成員方法,我們可以抽取一個類出來髓介,在抽取的類中聲明公共的部分
? 被抽取出來的類:父類惕鼓,基類,超類唐础,根類
? 兩個或者兩個以上的類:子類箱歧,派生類
? 他們之間的關(guān)系:子類 繼承自 父類
注意:
? a.object是所有類的父類矾飞,如果一個類沒有顯式指明它的父類,則默認為object
? b.簡化代碼呀邢,提高代碼的復(fù)用性
2.單繼承
2.1使用
簡單來說洒沦,一個子類只能有一個父類,被稱為單繼承
語法:
父類:
class 父類類名(object):
? 類體【所有子類公共的部分】
子類:
class 子類類名(父類類名):
? 類體【子類特有的屬性和成員方法】
說明:一般情況下价淌,如果一個類沒有顯式的指明父類申眼,則統(tǒng)統(tǒng)書寫為object
代碼演示:
person.py文件【父類】
#1.定義父類 class Person(object): #構(gòu)造函數(shù)【成員變量】 def __init__(self,name,age): self.name = name self.age = age #成員方法 def show(self): print("show") def __fun(self): print("fun")
worker.py文件【子類1】
from extends01.person import Person #2.定義子類 class Worker(Person): #構(gòu)造函數(shù)【成員變量】 def __init__(self,name,age,job): """ self.name = name self.age = age """ self.job = job #6.在子類的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù)【從父類中繼承父類中的成員變量】 #方式一:super(當前子類,self).__init__(屬性列表) #super(Worker, self).__init__(name,age) #方式二:父類名.__init__(self,屬性列表) Person.__init__(self,name,age) #方式三:super().__init__(屬性列表) #super().__init__(name,age) #成員方法 def work(self): print("work")
student.py文件【子類2】
from extends01.person import Person class Student(Person): # 構(gòu)造函數(shù)【成員變量】 def __init__(self, name, age, score): Person.__init__(self,name,age) self.score = score # 成員方法 def study(self): print("study")
extendsDemo01.py文件【測試模塊】
#測試模塊 from extends01.person import Person from extends01.worker import Worker from extends01.student import Student #3.創(chuàng)建父類的對象 p = Person("zhangsan",10) p.show() #p.__fun() #4.創(chuàng)建子類的對象 w = Worker("aaa",20,"工人") w.work() #5.子類對象訪問父類中的內(nèi)容 #結(jié)論一:子類對象可以調(diào)用父類中的公開的成員方法【因為繼承蝉衣,私有方法除外】 w.show() #w.__fun() #結(jié)論二:通過在子類的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù)括尸,子類對象可以直接訪問父類中的成員變量【私有變量除外】 print(w.name,w.age,w.job) s = Student("小明",9,90) s.study() s.show()
2.2特殊用法
代碼演示:
#6.子類中出現(xiàn)一個和父類同名的成員函數(shù),則優(yōu)先調(diào)用子類中的成員函數(shù) #子類的成員函數(shù)覆蓋了父類中的同名的成員函數(shù) s = Student("小明",9,90) s.study() s.show() #7.父類對象能不能訪問子類中特有的成員函數(shù)和成員變量?----->不能 per = Person("gs",10) #per.work()
#8.slots屬性能否應(yīng)用在子類中 #結(jié)論三:在父類中定義slots屬性限制屬性的定義病毡,子類中是無法使用濒翻,除非在子類中添加自己的限制 #父類 class Student(object): __slots__ = ("name","age") #子類 class SeniorStudent(Student): pass s = Student() s.name = "zhangsan" s.age = 10 #s.score = 90 ss = SeniorStudent() ss.name = "lisi" ss.age = 20 ss.score = 60
總結(jié):
繼承的特點:
? a.子類對象可以直接訪問父類中非私有化的屬性
? b.子類對象可以調(diào)用父類中非私有化的成員方法
? c.父類對象不能訪問或者調(diào)用子類 中任意的內(nèi)容
繼承的優(yōu)缺點:
優(yōu)點:
? a.簡化代碼,減少代碼的冗余
? b.提高代碼的復(fù)用性
? c.提高了代碼的可維護性
? d.繼承是多態(tài)的前提
缺點:
? 通常使用耦合性來描述類與類之間的關(guān)系啦膜,耦合性越低有送,則說明代碼的質(zhì)量越高
? 但是,在繼承關(guān)系中僧家,耦合性相對較高【如果修改父類雀摘,則子類也會隨著發(fā)生改變】
3.多繼承
一個子類可以有多個父類
語法:
class 子類類名(父類1,父類2啸臀,父類3.届宠。。乘粒。):
? 類體
代碼演示:
father.py文件【父類1】
class Father(object): def __init__(self,money): self.money = money def play(self): print("playing") def fun(self): print("father中的fun")
mother.py文件【父類2】
class Mother(object): def __init__(self,faceValue): self.faceValue = faceValue def eat(self): print("eating") def fun(self): print("mother中的fun")
child.py文件【子類】
from extends02.father import Father from extends02.mother import Mother #定義子類豌注,有多個父類 class Child(Mother,Father): def __init__(self,money,faceValue,hobby): #調(diào)用父類中的構(gòu)造函數(shù) Father.__init__(self,money) Mother.__init__(self,faceValue) self.hobby = hobby def study(self): print("study")
extendsDemo03.py文件【測試模塊】
from extends02.father import Father from extends02.mother import Mother from extends02.child import Child f = Father(100000) m = Mother(3.0) #創(chuàng)建子類對象 c = Child(1000,3.0,"打游戲") #子類對象調(diào)用父類中的成員方法 c.play() c.eat() #結(jié)論;如果多個父類中有相同的函數(shù),通過子類的對象調(diào)用灯萍,調(diào)用的是哪個父類中的函數(shù)取決于在父類列表中出現(xiàn)的先后順序 c.fun()
4.函數(shù)重寫【override】
在子類中出現(xiàn)和父類同名的函數(shù)轧铁,則認為該函數(shù)是對父類中函數(shù)的重寫
4.1系統(tǒng)函數(shù)重寫
__str__ __repr__
代碼演示:
class Animal(object): def __init__(self,name,age): self.name = name self.age = age #重寫__str__函數(shù),重寫之后一般return一個字符串,有關(guān)于成員變量 def __str__(self): return "name=%s age=%d"%(self.name,self.age) #重寫__repr__,作用和str是相同的旦棉,優(yōu)先使用str def __repr__(self): return "name=%s age=%d"%(self.name,self.age) a = Animal("大黃",10) print(a) #<__main__.Animal object at 0x00000226A87AC240> print(a.__str__()) #當一個類繼承自object的時候齿风,打印對象獲取的是對象的地址,等同于通過子類對象調(diào)用父類中__str__ #當打印對象的時候绑洛,默認調(diào)用了__str__函數(shù) #重寫__str__的作用:為了調(diào)試程序 """ 總結(jié):【面試題】 a.__str__和__repr__都未被重寫的時候救斑,使用對象調(diào)用的是__str__,此時__str__返回的是對象的地址 b.__str__和__repr__都被重寫之后,使用對象調(diào)用的是__str__真屯,此時__str__返回的是自定義的字符串 c.重寫了__str__脸候,但是沒有重寫__repr__,則使用對象調(diào)用的是__str__,此時__str__返回的是自定義的字符串 d.未重寫__str__运沦,但是重寫了__repr__泵额,則使用對象調(diào)用的是__repr__,此時,__repr__返回的是自定義的字符串 """ #使用時機:當一個對象的屬性有很多的時候携添,并且都需要打印嫁盲,則可以重寫__str__,可以簡化代碼烈掠,調(diào)試程序
4.2自定義函數(shù)重寫
代碼演示:
#函數(shù)重寫的時機:在繼承關(guān)系中羞秤,如果父類中函數(shù)的功能滿足不了子類的需求,則在子類中需要重寫 #父類 class People(object): def __init__(self,name): self.name = name def fun(self): print("fun") #子類 class Student(People): def __init__(self,name,score): self.score = score super(Student,self).__init__(name) #重寫;將函數(shù)的聲明和實現(xiàn)重新寫一遍 def fun(self): #在子類函數(shù)中調(diào)用父類中的函數(shù)【1.想使用父類中的功能向叉,2.需要添加新的功能】 #根據(jù)具體的需求決定需不需要調(diào)用父類中的函數(shù) super(Student,self).fun() print("fajfhak") s = Student("fhafh",10) s.fun()