4.4 精彩案例赏析
示例4-1 输入若干个成绩,求所有成绩的平均分。每输入一个成绩后询问是否继续输入下一个成绩,回答yes就继续输入下一个成绩,回答no就停止输入成绩。
numbers=[] #使用列表存放临时数据 while True: x=input(’请输入一个成绩:') try: #异常处理结构有关知识见第11章 numbers.append(float(x)) except: print(’不是合法成绩’) while True: flag=input(’继续输入吗?(yes/no)') if flag.lower() not in ('yes', 'no'): #限定用户输入内容必须为yes或no print(’只能输入yes或no') else: break if flag.lower()=='no': break print(sum(numbers)/len(numbers))
示例4-2 编写程序,判断今天是今年的第几天。
import time date=time.localtime() #获取当前日期时间 year, month, day=date[:3] day_month=[31,28,31,30,31,30,31,31,30,31,30,31] if year% 400==0 or (year% 4==0 and year% 100! =0): #判断是否为闰年 day_month[1]=29 if month==1: print(day) else: print(sum(day_month[:month-1])+day)
Python标准库datetime提供了datetime和timedelta对象可以很方便地计算指定年、月、日、时、分、秒之前或之后的日期时间,还提供了返回结果中包含“今天是今年第几天”“今天是本周第几天”等答案的timetuple()函数,等等。
>>>import datetime >>>Today=datetime.date.today() >>>Today datetime.date(2016,10,8) >>>Today-datetime.date(Today.year,1,1)+datetime.timedelta(days=1) datetime.timedelta(282) >>>Today.timetuple().tm_yday #今天是今年的第几天 282 >>>Today.replace(year=2013) #替换日期中的年 datetime.date(2013,10,8) >>>Today.replace(month=1) #替换日期中的月 datetime.date(2016,1,8) >>>now=datetime.datetime.now() >>>now datetime.datetime(2016,10,8,15,55,16,272174) >>>now.replace(second=30) #替换日期时间中的秒 datetime.datetime(2016,10,8,15,55,30,272174) >>>now+datetime.timedelta(days=5) #计算5天后的日期时间 datetime.datetime(2016,10,13,15,55,16,272174) >>>now+datetime.timedelta(weeks=-5) #计算5周前的日期时间 datetime.datetime(2016,9,3,15,55,16,272174) >>>def daysBetween(year1, month1, day1, year2, month2, day2): from datetime import date #计算两个日期之间相差多少天 dif=date(year1, month1, day1)-date(year2, month2, day2) return dif.days >>>daysBetween(2016,12,11,2016,11,27) 14 >>>daysBetween(2016,12,11,2011,11,27) 1841
另外,标准库calendar也提供了一些与日期操作有关的方法。例如:
>>>import calendar #导入模块 >>>print(calendar.calendar(2016)) #查看2016年的日历表,结果略 >>>print(calendar.month(2016,11)) #查看2016年11月份的日历表 >>>calendar.isleap(2016) #判断是否为闰年 True >>>calendar.weekday(2016,10,26) #查看指定日期是周几 2
当然,也可以自己编写代码模拟Python标准库calendar中查看日历的方法。
from datetime import date daysOfMonth=[31,28,31,30,31,30,31,31,30,31,30,31] def myCalendar(year, month): #获取year年month月1日是周几 start=date(year, month,1).timetuple().tm_wday #打印头部信息 print('{0}年{1}月日历’.format(year, month).center(56)) print(’日\t一\t二\t三\t四\t五\t六’) #获取该月有多少天,如果是2月并且是闰年,适当调整一下 day=daysOfMonth[month-1] if month==2: if year% 400==0 or (year% 4==0 and year% 100! =0): day+=1 #生成数据,根据需要在前面填充空白 result=[''*8 for i in range(start+1)] result+=list(map(lambda d: str(d).ljust(8), range(1, day+1))) #打印数据 for i, day in enumerate(result): if i! =0 and i% 7==0: print() print(day, end='') print() def main(year, month=-1): if type(year)! =int or year<1000 or year>10000: print('Year error') return if type(month)==int: #如果没有指定月份,就打印全年的日历 if month==-1: for m in range(1,13): myCalendar(year, m) #如果指定了月份,就只打印这一个月的日历 elif month in range(1,13): myCalendar(year, month) else: print('Month error') return else: print('Month error') return main(2017)
示例4-3 编写代码,输出由星号*组成的菱形图案,并且可以灵活控制图案的大小。
def main(n): for i in range(n): print(('* '*i).center(n*3)) for i in range(n,0, -1): print(('* '*i).center(n*3))
图4-5和图4-6分别为参数n=6和n=10的运行效果。
图4-5 n=6的运行效果
图4-6 n=10的运行效果
示例4-4 快速判断一个数是否为素数。
n=input("Input an integer:") n=int(n) if n==2: print('Yes') #偶数必然不是素数 elif n% 2==0: print('No') else: #大于5的素数必然出现在6的倍数两侧 #因为6x+2、6x+3、6x+4肯定不是素数,假设x为大于1的自然数 m=n %6 if m! =1 and m! =5: print('No') else: for i in range(3, int(n**0.5)+1,2): if n% i==0: print('No') break else: print('Yes')
示例4-5 编写程序,计算组合数C(n, i),即从n个元素中任选i个,有多少种选法。
根据组合数定义,可以编写代码如下:
import math def Cni1(n, i): return int(math.factorial(n)/math.factorial(i)/math.factorial(n-i))
虽然在Python中不用担心数字太大而超过变量的表示范围,但是计算大整数的阶乘也确实需要一些时间,尤其是上面的函数中存在大量的重复计算,严重影响速度。如果把组合数的定义展开并化简一下的话可以发现其中隐藏的规律,以Cni(8,3)为例,Cni(8,3)=8! /3! /(8-3)! =(8×7×6×5×4×3×2×1)/(3×2×1)/(5×4×3×2× 1),对于(5,8]区间的数,分子上出现一次而分母上没出现;(3,5]区间的数在分子、分母上各出现一次;[1,3]区间的数分子上出现一次而分母上出现两次。根据这一规律,可以编写如下非常高效的组合数计算程序。
def Cni2(n, i): if not (isinstance(n, int) and isinstance(i, int) and n>=i): print('n and i must be integers and n must be larger than or equal to i.') return result=1 Min, Max=sorted((i, n-i)) for i in range(n,0, -1): if i>Max: result*=i elif i<=Min: result/=i return result print(Cni2(6,2))
Python标准库itertools提供了组合函数combinations()、排列函数permutations()、用于循环遍历可迭代对象元素的函数cycle()、根据一个序列的值对另一个序列进行过滤的函数compress()、根据函数返回值对序列进行分组的函数groupby()、返回包含无限连续值的count对象的count函数()、计算笛卡儿积的函数product()等。下面的代码演示了部分函数的用法。
>>>import itertools >>>for it in itertools.combinations(range(1,5),3): #从4个元素中选3个元素的组合 print(it, end='') (1,2,3) (1,2,4) (1,3,4) (2,3,4) >>>list(itertools.permutations([1,2,3,4],3)) #从4个元素中任选3个元素的排列 >>>x=itertools.permutations([1,2,3,4],4) #4个元素的全排列 >>>for i in range(5): #输出前5个排列 print(next(x), end='') (1,2,3,4) (1,2,4,3) (1,3,2,4) (1,3,4,2) (1,4,2,3) >>>x='Private Key' >>>y=itertools.cycle(x) #循环遍历序列中的元素 >>>for i in range(20): print(next(y), end=', ') P, r, i, v, a, t, e, , K, e, y, P, r, i, v, a, t, e, , K, >>>for i in range(5): print(next(y), end=', ') e, y, P, r, i, >>>x=range(1,20) >>>y=(1,0)*9+(1, ) >>>y (1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1) >>>list(itertools.compress(x, y)) #根据一个序列的值,对另一个序列进行过滤 [1,3,5,7,9,11,13,15,17,19] >>>def group(v): if v>10: return'greater than 10' elif v<5: return'less than 5' else: return'between 5 and 10' >>>x=range(20) >>>y=itertools.groupby(x, group) #对序列元素进行分组 >>>for k, v in y: print(k, ':', list(v)) less than 5 : [0,1,2,3,4] between 5 and 10 : [5,6,7,8,9,10] greater than 10 : [11,12,13,14,15,16,17,18,19] >>>x=itertools.count(5,3) #起始值为5、步长为3的count对象 >>>for i in range(10): print(next(x), end='') 5 8 11 14 17 20 23 26 29 32 >>>list(zip('abcde', itertools.count())) #count对象中的元素个数是无穷的 [('a',0), ('b',1), ('c',2), ('d',3), ('e',4)] >>>list(zip('abc', itertools.count())) [('a',0), ('b',1), ('c',2)] >>>list(zip('abcdefghi', itertools.count())) [('a',0), ('b',1), ('c',2), ('d',3), ('e',4), ('f',5), ('g',6), ('h',7), ('i',8)] >>>for item in itertools.product('abc', range(4)): #笛卡儿积 print(item, end='') ('a',0) ('a',1) ('a',2) ('a',3) ('b',0) ('b',1) ('b',2) ('b',3) ('c',0) ('c',1) ('c',2) ('c',3) >>>for item in itertools.product('abc', '123', 'BC'): print(item, end='') ('a', '1', 'B') ('a', '1', 'C') ('a', '2', 'B') ('a', '2', 'C') ('a', '3', 'B') ('a', '3', 'C')('b', '1', 'B')('b', '1', 'C') ('b', '2', 'B') ('b', '2', 'C') ('b', '3', 'B') ('b', '3', 'C') ('c', '1', 'B') ('c', '1', 'C') ('c', '2', 'B') ('c', '2', 'C') ('c', '3', 'B') ('c', '3', 'C') >>>func=lambda x:x.isnumeric() >>>list(itertools.takewhile(func, '1234abcd')) #过滤元素 ['1', '2', '3', '4'] >>>list(itertools.dropwhile(func, '1234abcd')) ['a', 'b', 'c', 'd']
示例4-6 编写代码,模拟决赛现场最终成绩的计算过程。
while True: try: n=int(input(’请输入评委人数:')) if n<=2: print(’评委人数太少,必须多于2个人。') else: break except: pass scores=[] for i in range(n): #这个while循环用来保证用户必须输入0~100之间的数字 while True: try: score=input(’请输入第{0}个评委的分数:'.format(i+1)) #把字符串转换为实数 score=float(score) assert 0<=score<=100 scores.append(score) #如果数据合法,跳出while循环,继续输入下一个评委的分数 break except: print(’分数错误’) #计算并删除最高分与最低分 highest=max(scores) lowest=min(scores) scores.remove(highest) scores.remove(lowest) finalScore=round(sum(scores)/len(scores),2) formatter=’去掉一个最高分{0}\n去掉一个最低分{1}\n最后得分{2}' print(formatter.format(highest, lowest, finalScore))