明確項目目標:
互聯網公司有兩種角色,產品經理、程序員。產品經理提開發需求給程序員,然後程序員開發出滿足需求功能的程序。
今天我們來做個模擬,我們扮演“程序員”接到“產品經理”需求,並完成開發的過程。
需求文檔是這樣子的:
“敏捷開發”是互聯網的常態。剛剛當“程序員”的第一天,你就接到了這麼急的需求,那該怎麼辦呢?我們來分析一下。
分析過程,拆解項目
既然是個計算器程序,那就是要輸入信息後能計算出結果。為了搞清楚計算過程,我們需要根據案例倒推出計算公式。
我們先梳理一下需求文檔中的關鍵信息:
把程序版本大致規劃成三個階段:
逐步執行,代碼實現
要做一個“能用就好”的最基本的程序,我們可以直接編寫一個帶參數函數完成計算功能。程序寫出來大概長這個結構:
完善代碼的過程中,我們會用到第一個項目末尾提到的知識點:
格式化字符串。# 無需修改代碼,直接運行即可
# 工時計算
def estimated_time(size,number):
time = size * 80 / number
print('項目大小為%.1f個標準項目,使用%d個人力完成,則需要工時數量為:%.1f個' %(size,number,time))
# 人力計算
def estimated_numbeer(size,time):
number = size * 80 / time
print('項目大小為%.1f個標準項目,如果需要在%.1f個工時完成,則需要人力數量為:%d人' %(size,time,number))
# 調用工時計算函數
estimated_time(1.5,2)
# 調用人力計算函數
estimated_numbeer(0.8,25)
注:%f的意思是格式化字符串為浮點型,%.1f的意思是格式化字符串為浮點型,並保留1位小數。
第一步定位問題我們已經完成了:代碼的計算過程應該對對人數向上取整。也就是計算結果是1.5人的時候,取整數2,計算結果3.8人的時候,取整數4,計算結果10.1人的時候,取整數11……
第二步,我們可以直接尋找新知識,在搜索引擎中搜索“python 取整”,就能找到向上取整的函數。
其中import math是因為使用ceil()函數需要導入math模塊,就像第7關小遊戲項目中,使用randint()函數(隨機整數)需要導入random模塊。我們會在第15關詳細講解關於“模塊”的來龍去脈,現在你只需要讀懂代碼含義即可。
這個問題的解決還有另一種方式,在第二步的時候,你也可以運用已有知識解決。
有一個計算符不太常用,不知道你是否還記得:可以用%做取餘數運算,比如print(5%4)的結果是1(5除以4餘數為1)。
如果你想起了這個知識,那麼在第三步,我們可以找到一個切入點:如果人數不是整數(餘數不為零),就把人數用int()函數轉化為正數,然後再加1。
現在的問題又推進了一步:該如何設置條件,讓條件1代表人力計算,條件2代表工時計算?
這個問題有多種解法,關鍵點是利用參數設置條件。我先跟你演示一種:(請留意代碼註釋)
這裡的代碼用到了第9關默認參數的相關知識。在調用函數的時候,我們可以給指定的參數賦值,那剩餘的參數就會是默認值(也就是在定義函數的那行定義了他們的默認值)。比如estimated(size=1.5,time=20.0),給size和time賦值,那剩下的number就默認為None。
import math
# 為函數設置了三個參數,並都帶有默認參數
def estimated(size=1,number=None,time=None):
# 人力計算:如果參數中填了時間,沒填人數,就計算人力
if (number == None) and (time != None):
number = math.ceil(size * 80 / time)
print('項目大小為%.1f個標準項目,如果需要在%.1f個工時完成,則需要人力數量為:%d人' %(size,time,number))
# 工時計算:如果參數中填了人數,沒填時間,就計算工時
elif (number != None) and (time == None):
time = size * 80 / number
print('項目大小為%.1f個標準項目,使用%d個人力完成,則需要工時數量為:%.1f個' %(size,number,time))
# 調用函數的時候,傳遞兩個參數,會自動計算出第三個參數
estimated(size=1.5,number=2)
estimated(size=0.5,time=20.0)
剛才提到“合併成一個函數”這個問題不止一種解法。比如說,我們還可以這樣設置三個參數來實現相同的效果:
將以下代碼修改成剛才提到的第二種解決方案:
現在我們又拿到了新的需求:製作出“可以交互運行”的程序。
想讓程序可以交互,顯然要用input和print語句,這些都是我們曾經學過的。
為了方便,先把一些你可能需要複製的素材給你:
只要程序運行效果符合樣例演示效果即可。
到這裡,程序基本已經完成了。不過,為了展示用函數封裝代碼的精髓,我想再問大家一個問題:
可以創建一個主函數,用來調用幾個子函數。一起來溫習一下上個關卡的代碼:
用圖片來表示的話,是這樣的:
如圖所示,我們可以把每個獨立的功能封裝到每個單獨的函數中,然後用一個主函數打包這些單獨的函數,最後再調用主函數。
改造好了吧?看下參考代碼:
在這裡,myinput()函數負責跟用戶採集信息,estimated()函數負責完成計算,而main()函數把其他兩個函數打包放在一起並傳遞了參數。所以只要調用main()函數就能讓整個程序跑起來。你可以再次運行體驗一下。
import math
# 採集信息的函數
def myinput():
choice = input('請選擇計算類型:(1-人力計算,2-工時計算)')
if choice == '1':
size = float(input('請輸入項目大小:(1代表標準大小,請輸入小數)'))
number = None
time = float(input('請輸入工時數量:(請輸入小數)'))
return size,number,time
# 這裡返回的是一個元組
elif choice == '2':
size = float(input('請輸入項目大小:(1代表標準大小,請輸入小數)'))
number = int(input('請輸入人力數量:(請輸入整數)'))
time = None
return size,number,time
# 這裡返回的數據是一個元組
# 完成計算的函數
def estimated(my_input):
# 把元組中的數據取出來
size = my_input[0]
number = my_input[1]
time = my_input[2]
# 人力計算
if (number == None) and (time != None):
number = math.ceil(size * 80 / time)
print('項目大小為%.1f個標準項目,如果需要在%.1f個工時完成,則需要人力數量為:%d人' %(size,time,number))
# 工時計算
elif (number != None) and (time == None):
time = size * 80 / number
print('項目大小為%.1f個標準項目,使用%d個人力完成,則需要工時數量為:%.1f個' %(size,number,time))
# 主函數
def main():
my_input = myinput()
estimated(my_input)
# 調用主函數
main()
之所以寫成“子函數+主函數”的代碼結構,也是因為每個不同的功能封裝在單獨的函數代碼中,方便後續修改、增刪。
比如我們想要加一個功能“讓程序循環運行,直到用戶選擇結束”。那麼,就可以在程序中加上一個again函數。
import math
def myinput():
choice = input('請選擇計算類型:(1-人力計算,2-工時計算)')
if choice == '1':
size = float(input('請輸入項目大小:(1代表標準大小,請輸入小數)'))
number = None
time = float(input('請輸入工時數量:(請輸入小數)'))
return size,number,time
elif choice == '2':
size = float(input('請輸入項目大小:(1代表標準大小,請輸入小數)'))
number = int(input('請輸入人力數量:(請輸入整數)'))
time = None
return size,number,time
def estimated(my_input):
size = my_input[0]
number = my_input[1]
time = my_input[2]
if (number == None) and (time != None):
number = math.ceil(size * 80 / time)
print('項目大小為%.1f個標準項目,如果需要在%.1f個工時完成,則需要人力數量為:%d人' %(size,time,number))
elif (number != None) and (time == None):
time = size * 80 / number
print('項目大小為%.1f個標準項目,使用%d個人力完成,則需要工時數量為:%.1f個' %(size,number,time))
def main():
my_input = myinput()
estimated(my_input)
main()
比如我們想要加一個功能“讓程序循環運行,直到用戶選擇結束”。那麼,就可以在程序中加上一個again函數。
你可以先試試,看是否能夠自己完成這個函數的添加(注:可以複製到本地改造,完成後再複製上來)。
提示:1.需要新增變量和改造主函數;2.用到的知識是判斷和循環;3.對代碼進行調整是正常的(即不要期待總能一次成功)。
看下參考代碼:
循環運行程序的開關代碼:
import math
# 變量key代表循環運行程序的開關
key = 1
# 採集信息的函數
def myinput():
choice = input('請選擇計算類型:(1-工時計算,2-人力計算)')
if choice == '1':
size = float(input('請輸入項目大小:(1代表標準大小,請輸入小數)'))
number = int(input('請輸入人力數量:(請輸入整數)'))
time = None
return size,number,time
# 這裡返回的數據是一個元組
if choice == '2':
size = float(input('請輸入項目大小:(1代表標準大小,請輸入小數)'))
number = None
time = float(input('請輸入工時數量:(請輸入小數)'))
return size,number,time
# 這裡返回的是一個元組
# 完成計算的函數
def estimated(my_input):
# 把元組中的數據取出來
size = my_input[0]
number = my_input[1]
time = my_input[2]
# 人力計算
if (number == None) and (time != None):
number = math.ceil(size * 80 / time)
print('項目大小為%.1f個標準項目,如果需要在%.1f個工時完成,則需要人力數量為:%d人' %(size,time,number))
# 工時計算
elif (number != None) and (time == None):
time = size * 80 / number
print('項目大小為%.1f個標準項目,使用%d個人力完成,則需要工時數量為:%.1f個' %(size,number,time))
# 詢問是否繼續的函數
def again():
# 聲明全局變量key,以便修改該變量
global key
a = input('是否繼續計算?繼續請輸入y,輸入其他鍵將結束程序。')
if a != 'y':
# 如果用戶不輸入'y',則把key賦值為0
key = 0
# 主函數
def main():
print('歡迎使用工作量計算小程序!')
while key == 1:
my_input = myinput()
estimated(my_input)
again()
print('感謝使用工作量計算小程序!')
main()
到這裡,這一關就要收尾了。在本關我們模擬了“程序員”接“產品經理”需求的過程,開發出了一個迷你產品“工作量計算器”。同時,我們也對函數知識、解決問題的知識做了複習。最後,還給大家展示了“子函數+主函數”的編程結構。
不過,隨著我們編寫的代碼規模越大,越容易犯錯。編程中的報錯(俗稱bug)讓人最為惱火,特別是當程序運行不通過而你又定位不到問題的時候。
下一關,我會給你講如何調試(debug,即解決bug)的技巧,讓頭髮少掉,讓痛苦更輕,也更快過去。
練習目標:
我們會通過今天的作業,做出和電腦進行“石頭剪刀布”的遊戲。
練習要求:
和電腦玩一個剪刀石頭布的遊戲:電腦隨機出拳,我們可選擇出什麼。
首先,我們要讓雙方選擇出拳,才能判斷勝負。
我們可以設置變量computer_choice代表電腦的出拳選擇,設置變量user_choice代表你的出拳選擇。
電腦的出拳,我們可以使用random.choice()來隨機選擇;我們的出拳,可以手動輸入我們出拳的類型。
另外,判斷下輸入:當輸入的內容不是石頭剪刀布時,電腦會提醒'輸入有誤,請重新出拳',並重新出拳。
請根據已經設置好的代碼,補充代碼,讓代碼符合上面的要求。
你和電腦已經對自己要出的拳進行了選擇,接下來,我們需要知道雙方的出拳類型。
請使用print()函數補充亮拳的結果。
在前面兩步,電腦和你已經選擇完出拳的類型並亮拳後,只差最後一步:根據結果判斷勝負。
請將代碼補充完整,並運行幾次試試是否正確判斷
import random
# 出拳
punches = ['石頭','剪刀','布']
computer_choice = random.choice(punches)
user_choice = ''
user_choice = input('請出拳:(石頭、剪刀、布)') # 請用戶輸入選擇
while user_choice not in punches: # 當用戶輸入錯誤,提示錯誤,重新輸入
print('輸入有誤,請重新出拳')
user_choice = input()
# 亮拳
print('————戰鬥過程————')
print('電腦出了:%s' % computer_choice)
print('你出了:%s' % user_choice)
# 勝負
print('—————結果—————')
if user_choice == computer_choice: # 使用if進行條件判斷
print('平局!')
elif (user_choice == '石頭' and computer_choice == '剪刀') or (user_choice == '剪刀' and computer_choice == '布') or (user_choice == '布' and computer_choice == '石頭'):
print('你贏了!')
else:
print('你輸了!')
上一個練習的代碼中,有一個判斷語句的代碼很長很長:
elif (user_choice == '石頭' and computer_choice == '剪刀') or (user_choice == '剪刀' and computer_choice == '布') or (user_choice == '布' and computer_choice == '石頭'):
通過一個新的知識,將其簡化,體驗到“知識得增加,代碼得簡化”這個客觀規律。
index() 函數
index() 函數用於找出列表中某個元素第一次出現的索引位置。
語法為:list.index(obj),obj為object(對象)的縮寫。
具體可參考右側的代碼和運行結果。
num = [0,1,0,1,2]
print(num.index(1)) # 數字1首先出現的索引位置是list[1](索引位置從0開始)。
print(num.index(2)) # 數字2首先出現的索引位置是list[4]。
現在,請你根據新學的函數去簡化代碼吧。
相信你已經完成了代碼的簡化,不過,還是可以再看看參考代碼。
import random
# 出拳
punches = ['石頭','剪刀','布']
computer_choice = random.choice(punches)
user_choice = ''
user_choice = input('請出拳:(石頭、剪刀、布)') # 請用戶輸入選擇
while user_choice not in punches: # 當用戶輸入錯誤,提示錯誤,重新輸入
print('輸入有誤,請重新出拳')
user_choice = input()
# 亮拳
print('————戰鬥過程————')
print('電腦出了:%s' % computer_choice)
print('你出了:%s' % user_choice)
# 勝負
print('—————結果—————')
if user_choice == computer_choice: # 使用if進行條件判斷
print('平局!')
# 電腦的選擇有3種,索引位置分別是:0石頭、1剪刀、2布。
# 假設在電腦索引位置上減1,對應:-1布,0石頭,1剪刀,皆勝。
elif user_choice == punches[punches.index(computer_choice)-1]:
print('你贏了!')
else:
print('你輸了!')
閱讀更多 卓帥成長史OL 的文章