面向?qū)ο蟮倪M(jìn)階
包裝器:@property(getter)、@setter
之前我們討論過Python中屬性和方法訪問權(quán)限的問題,雖然我們不建議將屬性設(shè)置為私有的,但是如果直接將屬性暴露給外界也是有問題的,比如我們沒有辦法檢查賦給屬性的值是否有效。我們之前的建議是將屬性命名以單下劃線開頭,通過這種方式來暗示屬性是受保護(hù)的,不建議外界直接訪問,那么如果想訪問屬性可以通過屬性的getter(訪問器)和setter(修改器)方法進(jìn)行對(duì)應(yīng)的操作。如果要做到這點(diǎn),就可以考慮使用@property包裝器來包裝getter和setter方法,使得對(duì)屬性的訪問既安全又方便
__ slots __方法
如果需要限定自定義類型的對(duì)象只能綁定某些屬性,可以通過在類中定義slots變量來進(jìn)行限定。需要注意的是slots的限定只對(duì)當(dāng)前類的對(duì)象生效,對(duì)子類并不起任何作用
class Person(object):
# 限定Person對(duì)象只能綁定_name, _age和_gender屬性
__slots__ = ('_name', '_age', '_gender')
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
def play(self):
if self._age <= 16:
print('%s正在玩飛行棋.' % self._name)
else:
print('%s正在玩斗地主.' % self._name)
def main():
person = Person('王大錘', 22)
person.play()
person._gender = '男'
# AttributeError: 'Person' object has no attribute '_is_gay'
# person._is_gay = True
靜態(tài)方法和類方法
靜態(tài)方法:@staticmethod
類方法:@classmethod
類方法的第一個(gè)參數(shù)約定名為cls,它代表的是當(dāng)前類相關(guān)的信息的對(duì)象(類本身也是一個(gè)對(duì)象,有的地方也稱之為類的元數(shù)據(jù)對(duì)象),通過這個(gè)參數(shù)我們可以獲取和類相關(guān)的信息并且可以創(chuàng)建出類的對(duì)象
from math import sqrt
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
return self._a + self._b + self._c
def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))
def main():
a, b, c = 3, 4, 5
# 靜態(tài)方法和類方法都是通過給類發(fā)消息來調(diào)用的
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# 也可以通過給類發(fā)消息來調(diào)用對(duì)象方法但是要傳入接收消息的對(duì)象作為參數(shù)
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('無法構(gòu)成三角形.')
if __name__ == '__main__':
main()
from time import time, localtime, sleep
class Clock(object):
"""數(shù)字時(shí)鐘"""
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
def show(self):
"""顯示時(shí)間"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)
def main():
# 通過類方法創(chuàng)建對(duì)象并獲取系統(tǒng)時(shí)間
clock = Clock.now()
while True:
print(clock.show())
sleep(1)
clock.run()
if __name__ == '__main__':
main()
類之間的關(guān)系
1:is-a關(guān)系也叫繼承或泛化,比如學(xué)生和人的關(guān)系
2:has-a關(guān)系通常稱之為關(guān)聯(lián),比如汽車和引擎的關(guān)系都屬于關(guān)聯(lián)關(guān)系;關(guān)聯(lián)關(guān)系如果是整體和部分的關(guān)聯(lián),那么我們稱之為聚合關(guān)系;如果整體進(jìn)一步負(fù)責(zé)了部分的生命周期(整體和部分是不可分割的,同時(shí)同在也同時(shí)消亡),那么這種就是最強(qiáng)的關(guān)聯(lián)關(guān)系,我們稱之為合成關(guān)系。
3:use-a關(guān)系通常稱之為依賴,比如司機(jī)有一個(gè)駕駛的行為,其中使用到了汽車,那么司機(jī)和汽車的關(guān)系就是依賴關(guān)系
繼承和多態(tài)
在已有類的基礎(chǔ)上創(chuàng)建新類,這其中的一種做法就是讓一個(gè)類從另一個(gè)類那里將屬性和方法直接繼承下來,從而減少重復(fù)代碼的編寫。提供繼承信息的我們稱之為父類,也叫超類或基類;得到繼承信息的我們稱之為子類,也叫派生類或衍生類。子類除了繼承父類提供的屬性和方法,還可以定義自己特有的屬性和方法,所以子類比父類擁有的更多的能力,在實(shí)際開發(fā)中,我們經(jīng)常會(huì)用子類對(duì)象去替換掉一個(gè)父類對(duì)象,這是面向?qū)ο缶幊讨幸粋€(gè)常見的行為,對(duì)應(yīng)的原則稱之為里式替換原則,
子類在繼承了父類的方法后,可以對(duì)父類已有的方法給出新的實(shí)現(xiàn)版本,這個(gè)動(dòng)作稱之為方法重寫(override)。通過方法重寫我們可以讓父類的同一個(gè)行為在子類中擁有不同的實(shí)現(xiàn)版本,當(dāng)我們調(diào)用這個(gè)經(jīng)過子類重寫的方法時(shí),不同的子類對(duì)象會(huì)表現(xiàn)出不同的行為,這個(gè)就是多態(tài)
補(bǔ)充
函數(shù)的參數(shù)
必選參數(shù)
默認(rèn)值
可變參數(shù)(args)
關(guān)鍵字參數(shù)(*kwargs)
命名關(guān)鍵字參數(shù)
def f1(a,b,c=0,*args,**kw):
print(a,b,c,args,kw)
#調(diào)用情況:
f1(1,2)
f1(1,2,c=4)
f1(1,3,6,'aw','wad',x=123,y='1232')
其中:
a,b 為必選參數(shù)
c=0 為默認(rèn)參數(shù)
*args 為可變參數(shù),可變參數(shù)允許你傳入 0個(gè)或任意個(gè)參數(shù),這些可變參數(shù)在函數(shù)調(diào)用時(shí)自動(dòng)組裝為一個(gè)tuple
**kw 為關(guān)鍵字參數(shù),關(guān)鍵字參數(shù)允許你傳入 0個(gè)或任意個(gè)含參數(shù)名的參數(shù),這些關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動(dòng)組裝為一個(gè)dict
def person(name,age,*,city='hongkong',job='coder'):
print(name,age,city,job)
person('scofff',212,city='homy',job='eatter')
后面的兩個(gè)參數(shù)為命名關(guān)鍵字參數(shù)
對(duì)于關(guān)鍵字參數(shù),函數(shù)的調(diào)用者可以傳入任意不受限制的關(guān)鍵字參數(shù),至于到底傳入了哪些,就需要在函數(shù)內(nèi)部通過 kw 檢查。
與關(guān)鍵字參數(shù)**kw不同,命名關(guān)鍵字參數(shù)需要一個(gè)特殊分隔符*,*后面的參數(shù)被視為命名關(guān)鍵字參數(shù),如果沒有*號(hào),那么后面的參數(shù)將被視為普通的未位置參數(shù)。
命名關(guān)鍵字參數(shù)必須傳入?yún)?shù)名,而命名關(guān)鍵字參數(shù)可以有缺省值,這和位置參數(shù)不同。
面向?qū)ο蟮脑瓌t
單一職責(zé)原則
單一職責(zé)原則的定義是就一個(gè)類而言,應(yīng)該僅有一個(gè)引起他變化的原因。也就是說一個(gè)類應(yīng)該只負(fù)責(zé)一件事情。如果一個(gè)類負(fù)責(zé)了方法M1,方法M2兩個(gè)不同的事情,當(dāng)M1方法發(fā)生變化的時(shí)候,我們需要修改這個(gè)類的M1方法,但是這個(gè)時(shí)候就有可能導(dǎo)致M2方法不能工作。這個(gè)不是我們期待的,但是由于這種設(shè)計(jì)卻很有可能發(fā)生。所以這個(gè)時(shí)候,我們需要把M1方法,M2方法單獨(dú)分離成兩個(gè)類。讓每個(gè)類只專心處理自己的方法。
單一職責(zé)原則的好處如下:
1):可以降低類的復(fù)雜度,一個(gè)類只負(fù)責(zé)一項(xiàng)職責(zé),這樣邏輯也簡(jiǎn)單很多
2):提高類的可讀性,和系統(tǒng)的維護(hù)性,因?yàn)椴粫?huì)有其他奇怪的方法來干擾我們理解這個(gè)類的含義
3):當(dāng)發(fā)生變化的時(shí)候,能將變化的影響降到最小,因?yàn)橹粫?huì)在這個(gè)類中做出修改
開閉原則
開閉原則的定義是軟件中的對(duì)象(類,模塊,函數(shù)等)應(yīng)該對(duì)于擴(kuò)展是開放的,但是對(duì)于修改是關(guān)閉的。
當(dāng)需求發(fā)生改變的時(shí)候,我們需要對(duì)代碼進(jìn)行修改,這個(gè)時(shí)候我們應(yīng)該盡量去擴(kuò)展原來的代碼,而不是去修改原來的代碼,因?yàn)檫@樣可能會(huì)引起更多的問題。
這個(gè)準(zhǔn)則和單一職責(zé)原則一樣,是一個(gè)大家都這樣去認(rèn)為但是又沒規(guī)定具體該如何去做的一種原則。
開閉原則我們可以用一種方式來確保他,我們用抽象去構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。這樣當(dāng)發(fā)生修改的時(shí)候,我們就直接用抽象了派生一個(gè)具體類去實(shí)現(xiàn)修改
里氏替換原則
子類可以去擴(kuò)展父類的功能,但是不能改變父類原有的功能
1.子類可以實(shí)現(xiàn)父類的抽象方法,但是不能覆蓋父類的非抽象方法。
2.子類可以增加自己獨(dú)有的方法。
3.當(dāng)子類的方法重載父類的方法時(shí)候,方法的形參要比父類的方法的輸入?yún)?shù)更加寬松。
4.當(dāng)子類的方法實(shí)現(xiàn)父類的抽象方法時(shí),方法的返回值要比父類更嚴(yán)格。
合成聚合復(fù)用原則
合成/聚合復(fù)用原則經(jīng)常又叫做合成復(fù)用原則。該原則就是在一個(gè)新的對(duì)象里面使用一些已有的對(duì)象,使之成為新對(duì)象的一部分:新的對(duì)象通過向這些對(duì)象的委派達(dá)到復(fù)用已有功能的目的
迪米特法則
迪米特原則也被稱為最小知識(shí)原則
定義為一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最小的了解。
因?yàn)轭惻c類之間的關(guān)系越密切,耦合度越大,當(dāng)一個(gè)類發(fā)生改變時(shí),對(duì)另一個(gè)類的影響也越大,所以這也是我們提倡的軟件編程的總的原則:低耦合,高內(nèi)聚。
使用總結(jié):
(1).在類的劃分上,應(yīng)當(dāng)盡量創(chuàng)建松耦合的類,類之間的耦合度越低,就越有利于復(fù)用,一個(gè)處在松耦合中的類一旦被修改,不會(huì)對(duì)關(guān)聯(lián)的類造成太大波及;
(2).在類的結(jié)構(gòu)設(shè)計(jì)上,每一個(gè)類都應(yīng)當(dāng)盡量降低其成員變量和成員函數(shù)的訪問權(quán)限;
(3).在類的設(shè)計(jì)上,只要有可能,一個(gè)類型應(yīng)當(dāng)設(shè)計(jì)成不變類;
(4).在對(duì)其他類的引用上,一個(gè)對(duì)象對(duì)其他對(duì)象的引用應(yīng)當(dāng)降到最低。