一些關(guān)于自己學(xué)習(xí)Python的經(jīng)歷的內(nèi)容,遇到的問(wèn)題和思考等,方便以后查詢和復(fù)習(xí)。
聲明:本人學(xué)習(xí)是在扇貝編程通過(guò)網(wǎng)絡(luò)學(xué)習(xí)的,相關(guān)的知識(shí)、案例來(lái)源于扇貝編程。如果使用請(qǐng)說(shuō)明來(lái)源。
第26關(guān):類和面向?qū)ο蟮木幊?/b>
練習(xí):中獎(jiǎng)號(hào)碼
模板代碼是彩票抽獎(jiǎng)機(jī)的代碼,使用的是 面向過(guò)程 的思維方式寫的,思考一下用 面向?qū)ο?的思維如何改寫?寫完記得查看答案對(duì)比一下哦~
#from random import randint
#times = 6
#lottery = []
#for i in range(0, times):
#? number = randint(0, 99)
#? lottery.append(number)
#print('本期彩票中獎(jiǎng)號(hào)碼為:%s' % lottery)
#面向?qū)ο?/p>
from random import randint
# 由于需要隨機(jī)產(chǎn)生的數(shù)字,所以導(dǎo)入隨機(jī)生成函數(shù)是必須的。
class Lottery:? #定義一個(gè)類,這個(gè)我想到了,但是忘記了類的名稱首字母要大寫的要求了
? def __init__(self, times):? #定義一個(gè)初始化,參數(shù)處理self,還有一個(gè)是抽獎(jiǎng)次數(shù)。
? ? self.times = times? #設(shè)置初始化的特征
? ? self.lottery = []? # 設(shè)置一個(gè)空列表,因?yàn)槭浅跏蓟卣鳎郧懊婕觽€(gè)self
? def draw_lottery(self):? # 定義一個(gè)抽獎(jiǎng)的方法/行為
? ? for i in range(0, self.times):? #用for循環(huán),來(lái)個(gè)遍歷,只是范圍末端是一個(gè)參數(shù)
? ? ? number = randint(0, 99)
? ? ? self.lottery.append(number)
? def print_lottery(self):? ? # 定義一個(gè)打印結(jié)果的方法/行為
? ? print('本期彩票中獎(jiǎng)號(hào)碼為:%s' % self.lottery)
lottery = Lottery(6)? #實(shí)例化,同時(shí)對(duì)times賦值
lottery.draw_lottery()? # 調(diào)用實(shí)例化后的抽獎(jiǎng)方法
lottery.print_lottery()? #調(diào)用打印抽獎(jiǎng)結(jié)果的方法
說(shuō)實(shí)話,我是沒(méi)有弄出來(lái),通過(guò)前面的例子,我知道面向?qū)ο蟮淖兂伤悸肥侨齻€(gè)部分:第一個(gè)是定義一個(gè)類,第二個(gè)是實(shí)例化,第三個(gè)是調(diào)用。其中后兩個(gè)比較簡(jiǎn)單,第一個(gè)是比較難的。我看了前面那個(gè)關(guān)于學(xué)生姓名和成績(jī)的例子,但是我還是沒(méi)有思路如何做。
原因是前面學(xué)生成績(jī)的例子是個(gè)靜態(tài)的,就是資料時(shí)固定的,不變的。而這個(gè)是動(dòng)態(tài)的,號(hào)碼是沒(méi)有的。所以如何用定義類的方式來(lái)解決呢。我沒(méi)有思路,沒(méi)有辦法。只好看了答案。
發(fā)現(xiàn):三個(gè)步驟是找到了,第一個(gè)步驟是重點(diǎn),而且第一個(gè)步驟中就是抽即將的過(guò)程,所以這個(gè)步驟是對(duì)象中包含過(guò)程的,把整個(gè)抽獎(jiǎng)過(guò)程作為一個(gè)對(duì)象。
練習(xí):kakarotto
賽亞人會(huì)在月圓之夜因?yàn)樵铝涟l(fā)射出來(lái)的布爾茲光,進(jìn)化成巨猿。變身后是常態(tài)的 10 倍左右,并且會(huì)失去理智。
假設(shè)現(xiàn)在是月圓之夜,請(qǐng)你在類實(shí)例化時(shí)自動(dòng)調(diào)用 transform() 方法變身,并添加 __str__() 方法返回 我是來(lái)自xxx的xxx,代碼最終運(yùn)行結(jié)果應(yīng)如下:
變身巨猿
我是來(lái)自貝吉塔行星的卡卡羅特
要求:
給 Saiyan 類添加初始化方法,初始化方法接受 name 參數(shù)用于初始化 self.name,并調(diào)用 transform() 方法;
給 Saiyan 類添加 __str__() 方法,使打印 Saiyan 類的實(shí)例 kakarotto 時(shí)結(jié)果為 我是來(lái)自貝吉塔行星的卡卡羅特。
class Saiyan:
? def __init__(self, name):
? ? self.name = name
? ? self.born_place = '貝吉塔行星'
self.character = '天生好戰(zhàn)性格暴躁'?
#這兩個(gè)沒(méi)有賦值,還是特征,答案是放在外面的,我也考慮了這一點(diǎn),放在初始化外面更合理。放在這里前面加self,放在初始化外面,前面則不用加self
這兩個(gè)應(yīng)該也是賦值了,只不過(guò)似乎固定的值,而不是一個(gè)變量。
? def transform(self):
? ? print('變身巨猿')
? def __str__(self):
? ? return('我是來(lái)自%s的%s'%(self.born_place, self.name))
kakarotto = Saiyan('卡卡羅特')
kakarotto.transform() #答案是在初始化中調(diào)用這個(gè)方法的,所以最后就沒(méi)有了,符合題意
print(kakarotto)
變身巨猿
我是來(lái)自貝吉塔行星的卡卡羅特
根據(jù)答案更改之后的結(jié)果:
class Saiyan:
? born_place = '貝吉塔行星'
? character = '天生好戰(zhàn)性格暴躁'? #不在初始化中,所以前面不能再加self
? def __init__(self, name):
? ? self.name = name
? ? self.transform()? #可以在初始化中賦值,可以調(diào)用方法
? def __str__(self):
? ? return('我是來(lái)自%s的%s'%(self.born_place, self.name))
? def transform(self):
? ? print('變身巨猿')
kakarotto = Saiyan('卡卡羅特')
#kakarotto.transform()
print(kakarotto)
也可以這樣修改:
class Saiyan:
? def __init__(self, name, born_place, character):? 四個(gè)參數(shù)
? ? self.name = name
? ? self.transform()
? ? self.born_place = born_place
? ? self.character = character? 所有參數(shù)都進(jìn)行了初始化
? def __str__(self):
? ? return('我是來(lái)自%s的%s,%s'%(self.born_place, self.name, self.character))
? def transform(self):
? ? print('變身巨猿')
kakarotto = Saiyan('李建升','地球','天生愛(ài)吃肉')
#kakarotto.transform()
print(kakarotto)
變身巨猿
我是來(lái)自地球的李建升,天生愛(ài)吃肉
kakarotto = Saiyan('卡卡羅特','貝吉塔行星','天生天生好戰(zhàn)性格暴躁')
#kakarotto.transform()
print(kakarotto)
變身巨猿
我是來(lái)自貝吉塔行星的卡卡羅特,天生天生好戰(zhàn)性格暴躁
練習(xí):三頭六臂的哪吒
哪吒會(huì) 三頭六臂 的法術(shù),我們來(lái)給模板代碼中的類補(bǔ)充一個(gè)初始化方法,使其打印出 我叫哪吒,我有 3 頭 6 臂。
class Human:
? # 在這里補(bǔ)充初始化方法
? def __init__(self, name, heads, arms):
? ? self.name = name
? ? self.heads = heads
? ? self.arms = arms
? def intro(self):
? ? print('我叫{},我有{}頭{}臂'.format(self.name, self.heads, self.arms))
nezha = Human('哪吒', 3, 6)
nezha.intro()
練習(xí):三頭六臂的哪吒 另一個(gè)答案:
我們通過(guò) intro() 方法打印出了 我叫哪吒,我有 3 頭 6 臂。接下來(lái)我們?nèi)サ?intro() 方法,使用神奇方法 __str__() 來(lái)打印 我叫哪吒,我有 3 頭 6 臂。
class Human:
? def __init__(self, name, heads, arms):
? ? self.name = name
? ? self.heads = heads
? ? self.arms = arms
? def __str__(self):
? ? return '我叫{},我有{}頭{}臂'.format(self.name, self.heads, self.arms)
? #def intro(self):
? # print('我叫{},我有{}頭{}臂'.format(self.name, self.heads, self.arms))
nezha = Human('哪吒', 3, 6)
#nezha.intro()
print(nezha)
學(xué)習(xí)筆記:
初始化方法
在 Python 的類中,有一種特殊的方法——初始化方法。它的格式是 def __init__(self):,方法名由 init(initialize 的縮寫,初始化的意思)加左右兩邊的 雙下劃線 組成。
注意:初始化方法的 init 前后有 兩個(gè) 下劃線。
初始化方法的特殊之處是:每當(dāng)進(jìn)行類的實(shí)例化時(shí),初始化方法會(huì)自動(dòng)被執(zhí)行。
只是創(chuàng)建了實(shí)例,并沒(méi)有調(diào)用 __init__ 方法,它自己就自動(dòng)執(zhí)行了。利用這個(gè)特性,我們通常會(huì)在 初始化方法 里完成類屬性初始值的設(shè)置。
除了進(jìn)行固定的初始值設(shè)置,初始化方法 可以接收其他參數(shù),進(jìn)行自定義的屬性初始值設(shè)置。
class Human:
? def __init__(self, name, arms, legs, hair):
? ? # self.不能丟
? ? self.name = name
? ? self.arms = arms
? ? self.legs = legs
? ? self.hair = hair
? def walk(self):
? ? print('直立行走')
? def speak(self):
? ? print('說(shuō)著各式各樣的語(yǔ)言')
? def intro(self):
? ? print('我叫{},我有{}'.format(self.name, self.hair))
xiaobei = Human('小貝', 2, 2, '粉紅色的頭發(fā)')
print(xiaobei.name)
# 輸出:小貝
xiaobei.intro()
# 輸出:我叫小貝,我有粉紅色的頭發(fā)
給 初始化方法 額外添加了 4 個(gè)參數(shù),因此在實(shí)例化的時(shí)候要傳入對(duì)應(yīng)的值。Human('小貝', 2, 2, '粉紅色的頭發(fā)') 這里的 '小貝' 賦值給 self.name,兩個(gè) 2 分別賦值給 self.arms 和 self.legs,'粉紅色的頭發(fā)' 賦值給 self.hair。
當(dāng)實(shí)例化完成之后,初始化方法會(huì)自動(dòng)執(zhí)行,這樣我們就完成了自定義的屬性初始值設(shè)置。然后我們可以通過(guò) 實(shí)例名.屬性名 在類外訪問(wèn)或 self.屬性名 在類的方法中訪問(wèn)了。
神奇方法
像 __init__() 這樣的方法在 Python 的類中被稱為 神奇方法(或魔術(shù)方法),它們的特征是被 雙下劃線 所包裹。這一節(jié)中我們?cè)俳榻B一個(gè)神奇方法——__str__()。
class Human:
? arms = 2
? legs = 2
? hair = '各種顏色的頭發(fā)'
? def walk(self):
? ? print('直立行走')
? def speak(self):
? ? print('說(shuō)著各式各樣的語(yǔ)言')
? def intro(self):
? ? print('人類有%d條胳膊%d條腿' % (self.arms, self.legs))
# 類的實(shí)例化
human = Human()
human.intro()
# 輸出:人類有2條胳膊2條腿
class Human:
? arms = 2
? legs = 2
? hair = '各種顏色的頭發(fā)'
? def __str__(self):
? ? return '人類有%d條胳膊%d條腿' % (self.arms, self.legs)
? def walk(self):
? ? print('直立行走')
? def speak(self):
? ? print('說(shuō)著各式各樣的語(yǔ)言')
human = Human()
print(human)
# 輸出:人類有2條胳膊2條腿
可以看到,有了 __str__() 方法,直接打印實(shí)例的結(jié)果為 __str__() 方法的返回值。因此,我們可以使用 __str__() 方法來(lái)描述一個(gè)類。
面向?qū)ο笈c面向過(guò)程
與 面向?qū)ο?相對(duì)應(yīng)的是 面向過(guò)程,我們之前寫代碼都用的是 面向過(guò)程 的思維方式。也就是把一個(gè)問(wèn)題拆分成一個(gè)個(gè)步驟,然后用函數(shù)實(shí)現(xiàn)各個(gè)步驟,依次調(diào)用解決問(wèn)題。
而 面向?qū)ο?的思維方式是:把一個(gè)問(wèn)題拆分成各個(gè)對(duì)象,建立對(duì)象的目的不是為了完成一個(gè)步驟,而是為了描述某個(gè)事物在整個(gè)解決問(wèn)題的步驟中的行為和特征(方法和屬性)
可以看到,面向過(guò)程 是以動(dòng)作(函數(shù))為主體,對(duì)象(這個(gè)例子里是學(xué)生)作為參數(shù)傳遞給函數(shù)。而 面向?qū)ο?是以對(duì)象為主體,動(dòng)作和特征分別是對(duì)象的方法和屬性。用代碼來(lái)描述就是,面向過(guò)程:動(dòng)作(對(duì)象);面向?qū)ο螅簩?duì)象.動(dòng)作()。
使用 面向?qū)ο?的思維方式的好處是:程序的可讀性、可拓展性、可維護(hù)性高。但并不是說(shuō) 面向過(guò)程 就一無(wú)是處了,二者相輔相成,并不是對(duì)立的,我們要根據(jù)實(shí)際情況選擇合適的編程思維方式。
