吉吉:
选择与循环
条件表达式
- 在选择和循环结构中,都需要根据条件表达式的值确定下一步的执行流程。
True与False的判定
- 条件表达式的值只要不是False、0(或0.0、0j等)、空值None、空列表、空元组、空集合、空字典、空字符串、空range对象或其他空迭代对象,Python解释器均认为与True等价。
- 从这个意义上来讲,几乎所有的Python合法表达式都可以作为条件表达式,包括含有函数调用的表达式。
>>> if 3: #使用整数作为条件表达式
print(5)
5
>>> a = [1, 2, 3]
>>> if a: #使用列表作为条件表达式
print(a)
[1, 2, 3]
>>> a = []
>>> if a: #空列表视为False
else:
print('empty')
empty
>>> i = s = 0
>>> while i <= 10: #使用关系表达式作为条件表达式
s += i
i += 1
>>> print(s)
55
>>> i = s = 0
>>> while True:#使用常量True作为条件表达式
s += i
i += 1
if i > 10:
break
>>> print(s)
55
>>> s = 0
>>> for i in range(0, 11, 1): #遍历迭代对象中的所有元素
s += i
>>> print(s)
55
条件表达式中的常用运算符
- 关系运算符:>、<、==、<=、>=、!=,可以连续使用,以减少代码量
>>> 1<2<3
True
>>> 1<2>3
False
>>> 1<3>2
True
- 测试运算符:in、not in、is、is not
注意:==与is的区别
==是比较操作符,用来比较判断两个对象的value(值)是否相等。
is也被叫做同一性运算符,用来比较判断的是对象间的唯一身份标识id是否相同。
>>> x=3
>>> y=3
>>> x==y
True
>>> x is y
True
>>> x=[1,2,3]
>>> y=[1,2,3]
>>> x==y
True
>>> x is y
False
- 逻辑运算符“and”和“or”以及关系运算符,具有短路求值或惰性求值的特点,只计算必须计算的表达式的值。从而减少不必要的计算和判断。
- 在设计条件表达式时,如果能够大概预测不同条件失败的概率,并将多个条件根据“and”和“or”运算的短路求值特性进行组织,可以大幅度提高程序运行效率。
- 另外,在Python中,条件表达式中不允许使用赋值运算符“=”,避免了其他语言中误将关系运算符“==”写作赋值运算符“=”带来的麻烦,例如下面的代码,在条件表达式中使用赋值运算符“=”将抛出异常,提示语法错误。
>>> if a=3:
SyntaxError: invalid syntax
>>> if (a=3) and (b=4):
SyntaxError: invalid syntax
选择结构
单分支选择结构
-
格式:
if 表达式: #冒号不可缺少,下面的语句块要缩进
语句块
表达式与True等价,语句块执行,否则不执行。 -
输入使用空格分隔的两个整数,按升序输出。
x = input('Input two number:')
a, b = map(int, x.split())
if a > b:
a, b = b, a #序列解包,交换两个变量的值
print(a, b)
split():拆分字符串。通过指定分隔符对字符串(默认为空格)进行切片,并返回分割后的字符串列表(list)
双分支结构
格式:
if 表达式:
语句块1
else:
语句块2
>>> chTest = ['1', '2', '3', '4', '5']
>>> if chTest:
print(chTest)
else:
print('Empty')
['1', '2', '3', '4', '5']
- Python还支持如下形式的三元运算符,也可实现双分支:
value1 if condition else value2
当条件表达式condition的值与True等价时,表达式的值为value1,否则表达式的值为value2。另外,在value1和value2中还可以使用复杂表达式,包括函数调用和基本输出语句。
>>> a = 5
>>> print(6) if a>3 else print(5)
6
>>> print(6 if a>3 else 5)
6
>>> b = 6 if a>13 else 9
>>> b
9
- 这个三元运算符的表达式也具有惰性求值的特点。
#此时还没有导入math模块
>>> x = math.sqrt(9) if 5>3 else random.randint(1, 100)
NameError: name 'math' is not defined
>>> import math
#此时还没有导入random模块,但由于条件表达式5>3的值为True,所以可以正常运行
>>> x = math.sqrt(9) if 5>3 else random.randint(1,100)
#此时还没有导入random模块,由于条件表达式2>3的值为False,需要计算第二个表达式的值,因此出错
>>> x = math.sqrt(9) if 2>3 else random.randint(1, 100)
NameError: name 'random' is not defined
>>> import random
>>> x = math.sqrt(9) if 2>3 else random.randint(1, 100)
多分支结构
格式:
if 表达式1:
语句块1
elif 表达式2:
语句块2
elif 表达式3:
语句块3
else:
语句块4
其中,关键字elif是else if的缩写
- 利用多分支选择结构将成绩从百分制变换到等级制
def func(score):
if score > 100:
return 'wrong score.must <= 100.'
elif score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
elif score >= 60:
return 'D'
elif score >= 0:
return 'E'
else:
return 'wrong score.must >0'
选择结构的嵌套
- 格式:
if 表达式1:
语句块1
if 表达式2:
语句块2
else:
语句块3
else:
if 表达式4:
语句块4
注意:缩进必须要正确并且一致。
-
使用嵌套的选择结构实现百分制成绩到等级制的转换
def func(score):
degree = ‘DCBAAE‘ #[90,99]区间和100都对应A
if score > 100 or score < 0:
return ‘wrong score.must between 0 and 100.’
else:
index = (score - 60)//10#整除10
if index >= 0: #60分以上的成绩
return degree[index]
else:
return degree[-1] #60分以下的成绩对应F
构建跳转表实现多分支选择结构
- 使用列表、元组或字典可以很容易构建跳转表,在某些场合下可以更快速地实现类似于多分支选择结构的功能。
funcDict = {'1':lambda:print('You input 1'),
'2':lambda:print('You input 2'),
'3':lambda:print('You input 3')}
x = input('Input an integer to call different function:')
func = funcDict.get(x, None)
if func:
func()
else:
print('Wrong integer.')
选择结构应用
- 面试资格确认。
age=24
subject="计算机"
college="非重点"
if (age > 25 and subject=="电子信息工程") or (college=="重点" and subject=="电子信息工程" ) or (age<=28 and subject=="计算机"):
print("恭喜,你已获得我公司的面试机会!")
else:
print("抱歉,你未达到面试要求")
-
编写程序,判断今天是今年的第几天。
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%4000 or (year%40 and year%100!=0): #判断是否为闰年
day_month[1] = 29if month==1:
print(day)
else:
print(sum(day_month[:month-1])+day)
说明:time.localtime([secs]):将一个时间戳转换为当前时区的struct_time, struct_time元组共有9个元素
- python 中与时间处理相关的模块包括 time、datetime、以及 calendar
其中闰年判断也可以直接使用calendar模块的方法。
>>> calendar.isleap(2016)
True
>>> calendar.isleap(2015)
False
- 或者使用datetime模块提供的功能来直接计算今天是今年的第几天
>>> datetime.date.today().timetuple().tm_yday
302
>>> datetime.date(2018,10,25).timetuple().tm_yday
298
请自行查阅datetime模块使用方法
- datetime还提供了其他功能
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2015, 12, 6, 16, 1, 6, 313898)
>>> now.replace(second=30) #替换日期时间中的秒
datetime.datetime(2015, 12, 6, 16, 1, 30, 313898)
>>> now+datetime.timedelta(days=5) #计算5天后的日期时间
datetime.datetime(2015, 12, 11, 16, 1, 6, 313898)
>>> now + datetime.timedelta(weeks=-5) #计算5周前的日期时间
datetime.datetime(2015, 11, 1, 16, 1, 6, 313898)
循环结构
- Python提供了两种基本的循环结构语句——while语句、for语句。
- while循环一般用于循环次数难以提前确定的情况,也可以用于循环次数确定的情况。
- for循环一般用于循环次数可以提前确定的情况,尤其是用于枚举序列或迭代对象中的元素。
一般优先考虑使用for循环。
- 相同或不同的循环结构之间都可以互相嵌套,实现更为复杂的逻辑。
- for循环与while循环
while 条件表达式:
循环体
[else: #如果循环由break跳出则不执行else内部代码
else子句代码块]
for 取值 in 序列或迭代对象:
循环体
[else:
else子句代码块]
循环结构的优化
- 为了优化程序以获得更高的效率和运行速度,在编写循环语句时,应尽量减少循环内部不必要的计算,将与循环变量无关的代码尽可能地提取到循环之外。对于使用多重循环嵌套的情况,应尽量减少内层循环中不必要的计算,尽可能地向外提。
- 优化前的代码:
#生成由1,2,3,4组成的所有3位数,为观察效率重复1000次
digits = (1, 2, 3, 4)
for i in range(1000): #假设执行1000次
result = []
for i in digits:
for j in digits:
for k in digits:
result.append(i*100+j*10+k) # 循环内部被重复计算
- 优化后的代码:
for i in range(1000):
result = []
for i in digits:
i = i*100
for j in digits:
j = j*10
for k in digits:
result.append(i+j+k)
- 另外,在循环中应尽量引用局部变量,因为局部变量的查询和访问速度比全局变量略块,在使用模块中的方法时,可以通过将其转换为局部变量来提高运行速度。
import time
import math
start = time.time() #获取当前时间
for i in range(10000000):
math.sin(i)
print('Time Used:', time.time()-start) #输出所用时间
loc_sin = math.sin #用内部变量直接指向该模块函数
start = time.time()
for i in range(10000000):
loc_sin(i)
print('Time Used:', time.time()-start)
break和continue语句
- break语句在while循环和for循环中都可以使用,一般放在if选择结构中,一旦break语句被执行,将使得整个循环提前结束。
- continue语句的作用是终止当前循环,并忽略continue之后的语句,然后回到循环的顶端,提前进入下一次循环。
- 除非break语句让代码更简单或更清晰,否则不要轻易使用。
- 下面的代码用来计算小于100的最大素数,注意break语句和else子句的用法。
for n in range(100, 1, -1): #遍历范围是[100,2],由大到小遍历
for i in range(2, n):
if n%i == 0:
break
else:
print(n)
break
97
- 删除上面代码中最后一个break语句,则可以用来输出100以内的所有素数。
>>> for n in range(100, 1, -1):
for i in range(2, n):
if n%i == 0:
break
else:
print(n, end=' ')
97 89 83 79 73 71 67 61 59 53 47 43 41 37 31 29 23 19 17 13 11 7 5 3 2
-
警惕continue可能带来的问题:
i=0
while i<10:
if i%2==0:
continue
print(i)
i+=1 #continue 后不执行,i不变成死循环
永不结束的死循环,Ctrl+C强行终止。
- 这样子就不会有问题
>>> for i in range(10): #遍历自动修改i值
if i%2==0:
continue
print(i, end=' ')
1 3 5 7 9
>>> for i in range(10):
if i%2==0:
i+=1#其实没有用
continue
print(i, end=' ')
1 3 5 7 9
- 每次进入循环时的i已经不再是上一次的i,所以修改其值并不会影响循环的执行。
>>> for i in range(7):
print(id(i),':',i)
10416692 : 0
10416680 : 1
10416668 : 2
10416656 : 3
10416644 : 4
10416632 : 5
10416620 : 6
打印九九乘法表。
for i in range(1,10):
for j in range(1,i+1):
print('{0}*{1}={2}'.format(i,j,i*j).ljust(6), end=' ')
print()
说明:ljust(n) 方法返回一个原字符串左对齐,并使用空格填充至指定长度为n的新字符串。
- 编写程序,计算组合数C(n,i),即从n个元素中任选i个,有多少种选法。
根据组合数定义,需要计算3个数的阶乘,在很多编程语言中都很难直接使用整型变量表示大数的阶乘结果,虽然Python并不存在这个问题,但是计算大数的阶乘仍需要相当多的时间。本例提供另一种计算方法:以Cni(8,3)为例,按定义式展开为Cni(8,3)=8!/3!/(8-3)!=(87654321) /(321)/ (5432*1),对于(5,8]区间的数,分子上出现一次而分母上没出现;(3,5]区间的数在分子、分母上各出现一次;[1,3]区间的数分子上出现一次而分母上出现两次。
def Cni1(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
- 也可以使用math库中的阶乘函数直接按组合数定义实现。
>>> def Cni2(n, i):
import math
return int(math.factorial(n)/math.factorial(i)/math.factorial(n-i))
>>> Cni2(6,2)
15
- 还可以直接使用Python标准库itertools提供的函数。
>>> import itertools
>>> len(tuple(itertools.combinations(range(60),2)))
1770
- itertools还提供了排列函数permutations()。
>>> import itertools
>>> for item in itertools.permutations(range(1,4),2):
print(item)
(1, 2)
(1, 3)
(2, 1)
(2, 3)
(3, 1)
(3, 2)