學習python課程第二十三天
阿新 • • 發佈:2018-10-24
編寫程序 fun inpu level @property ini 代碼冗余 單純 student
一. 組合 :
1. 什麽是組合 ?
一個對象的屬性是來自於另外一個類的對象, 稱之為組合. (跟繼承其實很相似.都是共用一個類裏面的屬性)
2. 為何用組合 ?
組合也是用來解決類與類代碼冗余的問題.
3. 如何用組合 ?
class Foo:
aaa=1111
def __init__(self,x,y):
self.x=x
self.y=y
def func1(self):
print(‘Foo內的功能‘)
class Bar:
bbb=2222
def __init__(self, m, n):
self.m = m
self.n = n
def func2(self):
print(‘Bar內的功能‘)
obj1=Foo(10,20)
obj2=Bar(30,40)
obj1.xxx=obj2 (把兩個類組合到了一起.)
print(obj1.x,obj1.y,obj1.aaa,obj1.func1)
print(obj1.xxx.m,obj1.xxx.n,obj1.xxx.bbb,obj1.xxx.func2)
小練習 :
class OldboyPeople:
school = ‘Oldboy‘
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class OldboyStudent(OldboyPeople):
def choose_course(self):
print(‘%s is choosing course‘ %self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self, name, age, gender,level,salary):
OldboyPeople.__init__(self, name, age, gender)
self.level=level
self.salary=salary
def score(self,stu,num):
stu.num=num
print(‘老師%s給學生%s打分%s‘ %(self.name,stu.name,num))
class Course:
def __init__(self,course_name,course_price,course_period):
self.course_name=course_name
self.course_price=course_price
self.course_period=course_period
def tell_course(self):
print(‘課程名:<%s> 價錢:[%s] 周期:[%s]‘ % (self.course_name, self.course_price, self.course_period))
python_obj=Course(‘python開發‘,3000,‘5mons‘)
linux_obj=Course(‘linux運維‘,5000,‘3mons‘)
stu1=OldboyStudent(‘egon‘,18,‘male‘)
stu1.courses=[]
stu1.courses.append(linux_obj)
stu1.courses.append(python_obj)
stu1.courses[0].tell_course()
stu2=OldboyStudent(‘kevin‘,38,‘male‘)
二. 封裝 :
1. 什麽是封裝 ?
裝,指的是把屬性裝進一個容器,
封,指的是隱藏的意思,但是這種隱藏是對外不對內的.
2. 為何要封裝 ?
封裝不是單純意義的隱藏,
封裝數據屬性的目的 : 將數據屬性封裝起來, 類外部的使用就無法直接操作該數據屬性了.
需要類內部開一個接口給使用者,類的設計者可以在接口之上附加任意邏輯,從而嚴格控制使用者對屬性的操作.
封裝函數屬性的目的 :隔離復雜度.
3. 如何封裝 ?
只需要在屬性前加上__開頭,該屬性就會被隱藏起來,該隱藏具備的特點 :
1. 只是一種語法意義上的變形, 即__開頭的屬性會在檢測語法時發生變形為 _類名__屬性名的形式
2. 這種隱藏式對外不對內的, 因為在類內部檢測語法時, 所有的代碼統一都發生了變形.
3. 這種變形只在檢測語法時發生一次, 在類定義之後新增的__開頭的屬性並不會發生變形.
4. 如果父類不想讓子類覆蓋自己的屬性, 可以在屬性前加__開頭
註意:
這種機制也並沒有真正意義上的限制我們從外部直接訪問屬性, 知道了類名和屬性名就可以拼出名字 :
_類名__屬性名, 然後就可以訪問了, 如a._A__N, 即這種操作並不是嚴格意義上的限制外部訪問, 僅僅是
一種語法意義上的變形, 主要用來限制外部的直接訪問.
示例:
1. 封裝數據 : 將數據隱藏起來並不是目的. 隱藏起來然後對外提供該數據的接口, 然後我們可以在接口附加上對該數據操作的
限制, 以此完成對數據屬性操作的嚴格控制.
class Teacher: def __init__(self,name,age): # self.__name=name # self.__age=age self.set_info(name,age) def tell_info(self): print(‘姓名:%s,年齡:%s‘ %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError(‘姓名必須是字符串類型‘) if not isinstance(age,int): raise TypeError(‘年齡必須是整型‘) self.__name=name self.__age=age t=Teacher(‘egon‘,18) t.tell_info() t.set_info(‘egon‘,19) t.tell_info()
2. 封裝函數 : 目的是隔離復雜度.
提示 :在編程語言裏, 對外提供的接口 (接口可理為一個入口), 可以是函數, 稱為接口函數, 這與接口的概念還不一樣,
接口代表一組接口函數的集合體.
#取款是功能,而這個功能有很多功能組成:插卡、密碼認證、輸入金額、打印賬單、取錢 #對使用者來說,只需要知道取款這個功能即可,其余功能我們都可以隱藏起來,很明顯這麽做 #隔離了復雜度,同時也提升了安全性 class ATM: def __card(self): print(‘插卡‘) def __auth(self): print(‘用戶認證‘) def __input(self): print(‘輸入取款金額‘) def __print_bill(self): print(‘打印賬單‘) def __take_money(self): print(‘取款‘) def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
封裝與擴展性:
封裝在於明確區分內外, 使得類實現者可以修改封裝內的東西,而不是影響外部調用者的代碼. 而外部使用者只知道一個接口
(函數), 只要接口(函數)名,參數不變,使用者的代碼永遠無需改變, 這就提供一個良好的合作基礎, 或者說,只要這個基礎約定
不變,則代碼改變不足為慮
三. 特性(property)裝飾器:
什麽是特性property
property是一種特殊的屬性,訪問它時會執行一段功能(函數) 然後返回值
為什麽要用property :
將一個類的函數定義後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,
這種特性的使用方式遵循了統一訪問的原則
property 下面還有裝飾器. @setter.(可以修改屬性) @deleter.(可以刪除屬性)
示例 :
BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更
便於理解)
成人的BMI數值: 過輕:低於18.5 正常:18.5-23.9 過重:24-27 肥胖:28-32 非常肥胖, 高於32 體質指數(BMI)=體重(kg)÷身高^2(m)class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight / (self.height**2) p1=People(‘egon‘,75,1.85) print(p1.bmi)
property 的兩種使用方法.
class People:
def __init__(self,name):
self.__name=name
@property
def name(self):
return ‘<name:%s>‘ %self.__name
@name.setter
def name(self,new_name):
if type(new_name) is not str:
print(‘名字必須是str類型‘)
return
self.__name=new_name
@name.deleter
def name(self):
del self.__name
obj=People(‘egon‘)
print(obj.name)
del obj.name
print(obj.__dict__)
class People:
def __init__(self,name):
self.__name=name
def xxx_name(self):
return ‘<name:%s>‘ %self.__name
def yyy_name(self,new_name):
if type(new_name) is not str:
print(‘名字必須是str類型‘)
return
self.__name=new_name
def zzz_name(self):
del self.__name
name=property(xxx_name,yyy_name,zzz_name)
obj=People(‘egon‘)
print(obj.name)
del obj.name
print(obj.__dict_
四. 多態性.
多態性分為靜態多態性與動態多態性.
靜態多態性 :如任何類型都可以用運算符進行運算.
動態多態性 : 如下
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是動物,只要是動物肯定有talk方法 #於是我們可以不用考慮它們三者的具體是什麽類型,而直接使用 peo.talk() dog.talk() pig.talk() #更進一步,我們可以定義一個統一的接口來使用 def func(obj): obj.talk()
多態性的好處 :
1, 增加了程序的靈活性 :
以不變應萬變,不論對象千變萬化,使用者都是同一種形式去調用, 如func(animal)
2, 增加了程序的可擴展性
通過集成animal類創建了一個新的類, 使用者無需更改自己的代碼. 還是用func(animal)去調用
示例:
class Cat(Animal): #屬於動物的另外一種形態:貓 def talk(self): print(‘say miao‘) def func(animal): #對於使用者來說,自己的代碼根本無需改動 animal.talk() cat1=Cat() #實例出一只貓 func(cat1) #甚至連調用方式也無需改變,就能調用貓的talk功能 say miao
這樣我們新增了一個形態Cat,由Cat類產生的實例cat1,使用者可以在完全不需要修改自己代碼的情況下。
使用和人、狗、豬一樣的方式調用cat1的talk方法,即func(cat1)
鴨子類型 :
Python崇尚鴨子類型,即如果看起來像,叫聲像而且走起路來像鴨子, name它就是鴨子,
python程序員通常根據這種行為來編寫程序, 例如,如果想編寫現有對象的自定義版本,可以繼承該對象也可以
創建一個外觀和行為像,但與它無任何關系的全新對象,後者通常用於保存程序組件的松耦合度.
示例一 :
利用標準庫中定義的各種‘與文件類似‘的對象, 盡管這些對象的工作方式像文件,但他們沒有繼承內置文件對象的方法.
#二者都像鴨子,二者看起來都像文件,因而就可以當文件一樣去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass
示例二. :
其實大家一直在享受著多態性帶來的好處,比如Python的序列類型有多種形態:字符串,列表,元組. 多態性體現如下
#str,list,tuple都是序列類型 s=str(‘hello‘) l=list([1,2,3]) t=tuple((4,5,6)) #我們可以在不考慮三者類型的前提下使用s,l,t s.__len__() l.__len__() t.__len__() len(s) len(l) len(t
學習python課程第二十三天