流程控制

迄今为止,我们写的Python代码都是一条一条语句顺序执行,这种代码结构通常称之为顺序结构。然而仅有顺序结构并不能解决所有的问题,比如我们设计一个游戏,游戏第一关的通关条件是玩家获得1000分,那么在完成本局游戏后,我们要根据玩家得到分数来决定究竟是进入第二关,还是告诉玩家“Game Over”,这里就会产生两个分支,而且这两个分支只有一个会被执行。类似的场景还有很多,我们将这种结构称之为“分支结构”或“选择结构”。

除了分支结构外,还有循环结构。如果在程序中我们需要重复的执行某条或某些指令,例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向奔跑的指令。当然你可能已经注意到了,刚才的描述中不仅仅有需要重复的动作,还需要用到分支结构。再举一个简单的例子,我们要实现一个每隔1秒中在屏幕上打印一次”hello, world”并持续打印一个小时的程序,我们肯定不能够直接把print(‘hello, world’)这句代码写3600遍,如果真的要这样做,那么编程的工作就太无聊乏味了。因此,我们还需要了解一下循环结构,有了循环结构我们就可以轻松的控制某件事或者某些事重复、重复、再重复的去执行。

再谈print

print可用于打印一个表达式,这个表达式要么是字符串,要么将自动转换为字符串。但实际上,你可同时打印多个表达式,条件是用逗号分隔它们:

  1. >>> print('Age:', 42)
  2. Age: 42

print打印字符串会打印不带引号的内容, 并且在参数项之间会插入一个空格。

  1. >>> name = 'Gumby'
  2. >>> salutation = 'Mr.'
  3. >>> greeting = 'Hello,'
  4. >>> print(greeting, salutation, name)
  5. Hello, Mr. Gumby

如果字符串变量greeting不包含逗号,如何在结果中添加呢?

  1. >>> name = 'Gumby'
  2. >>> salutation = 'Mr.'
  3. >>> greeting = 'Hello'
  4. >>> print(greeting + ',', salutation, name)
  5. Hello, Mr. Gumby

还可以自定义分隔符

  1. >>> print("I", "wish", "to", "register", "a", "complaint", sep="_")
  2. I_wish_to_register_a_complaint

还可自定义结束字符串,以替换默认的换行符。

  1. >>> i = 1
  2. >>> while i < 10:
  3. ... print(i,end=',')
  4. ... i = i+1
  5. ...
  6. 1,2,3,4,5,6,7,8,9,

赋值魔法

即便是不起眼的赋值语句也蕴藏着一些使用窍门。

序列解包

赋值语句你见过很多,有的给变量赋值,还有的给数据结构的一部分(如列表中的元素和切片,或者字典项)赋值,但还有其他类型的赋值语句。例如,可同时(并行)给多个变量赋值:

  1. >>> x, y, z = 1, 2, 3
  2. >>> print(x, y, z)
  3. 1 2 3

有什么用呢?可用来交换多个变量的值:

  1. >>> man = '鹿晗'
  2. >>> women = '林志玲'
  3. >>> man , women = women , man
  4. >>> print(man,women)
  5. 林志玲 鹿晗

实际上,这里执行的操作称为序列解包(或可迭代对象解包):将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中。

  1. >>> team = '唐僧','孙悟空','猪八戒','沙和尚'
  2. >>> team
  3. ('唐僧', '孙悟空', '猪八戒', '沙和尚')
  4. >>> a,b,c,d = team
  5. >>> a
  6. '唐僧'
  7. >>> b
  8. '孙悟空'
  9. >>> c
  10. '猪八戒'
  11. >>> d
  12. '沙和尚'

这在使用返回元组(或其他序列或可迭代对象)的函数或方法时很有用。假设要从字典中随便获取(或删除)一个键值对,可使用方法popitem,它随便获取一个键值对并以元组的方式返回。接下来,可直接将返回的元组解包到两个变量中。

  1. >>> scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
  2. >>> key, value = scoundrel.popitem()
  3. >>> key
  4. 'girlfriend'
  5. >>> value
  6. 'Marion'

这让函数能够返回被打包成元组的多个值,然后通过一条赋值语句轻松地访问这些值。

要解包的序列包含的元素个数必须与你在等号左边列出的目标个数相同,否则Python将引发异常。

  1. >>> x,y,z = 1,2
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. ValueError: not enough values to unpack (expected 3, got 2)
  5. >>> x,y = 1,2,3
  6. Traceback (most recent call last):
  7. File "<stdin>", line 1, in <module>
  8. ValueError: too many values to unpack (expected 2)

可使用星号运算符(*)来收集多余的值,这样无需确保值和变量的个数相同,如下例所示:

  1. >>> a, b, *rest = [1, 2, 3, 4]
  2. >>> rest
  3. [3, 4]

还可将带星号的变量放在其他位置。

  1. >>> team = "唐僧 孙悟空 猪八戒 沙和尚 白龙马"
  2. >>> shifu,*tudi,zuoji = team.split()
  3. >>> shifu
  4. '唐僧'
  5. >>> tudi
  6. ['孙悟空', '猪八戒', '沙和尚']
  7. >>> zuoji
  8. '白龙马'

赋值语句的右边可以是任何类型的序列,但带星号的变量最终包含的总是一个列表。在变量和值的个数相同时亦如此。

  1. >>> a, *b, c = '123'
  2. >>> a
  3. '1'
  4. >>> b
  5. ['2']
  6. >>> c
  7. '3'

链式赋值

链式赋值是一种快捷方式,用于将多个变量关联到同一个值。

  1. >>> x = y = [1,2,3]
  2. #上述代码与下面的代码等价:
  3. >>> x = [1,2,3]
  4. >>> x = y
  5. #请注意,这两条语句可能与下面的语句不等价:
  6. >>> x = [1,2,3]
  7. >>> y = [1,2,3]

增强赋值

可以不编写代码x = x + 1,而将右边表达式中的运算符(这里是+)移到赋值运算符(=)前面,从而写成x += 1。这称为增强赋值,适用于所有标准运算符,如*、/、%等。

  1. >>> x = 2
  2. >>> x += 1
  3. >>> x
  4. 3
  5. >>> x *= 2
  6. >>> x
  7. 6

增强赋值也可用于其他数据类型(只要使用的双目运算符可用于这些数据类型)。

  1. #通过使用增强赋值,可让代码更紧凑、更简洁,同时在很多情况下的可读性更强。
  2. >>> tianwang = '周杰伦'
  3. >>> tianwang += '酷毙了'
  4. >>> tianwang
  5. '周杰伦酷毙了'
  6. >>> tianwang *= 2
  7. >>> tianwang
  8. '周杰伦酷毙了周杰伦酷毙了'

代码块:缩进

  • 代码块是一组语句,可在满足条件时执行(if语句),可执行多次(循环),等等。
  • 代码块是通过缩进代码(即在前面加空格)来创建的。
  • 也可使用制表符来缩进代码块。Python将制表符解释为移到下一个制表位(相邻制表位相距8个空格),但标准(也是更佳的)做法是只使用空格(而不使用制表符)来缩进,且每级缩进4个空格。
  • 在同一个代码块中,各行代码的缩进量必须相同。
  • 在Python中,使用冒号(:)指出接下来是一个代码块,并将该代码块中的每行代码都缩进相同的程度。

布尔值

布尔值就是真(True)和假(False)。

  1. #用作布尔表达式(如用作if语句中的条件)时,下面的值都将被解释器视为假:
  2. >>> False None 0 "" () [] {}
  3. #标准值False和None、各种类型(包括浮点数、复数等)的数值0、空序列(如空字符串、空元组和空列表)
  4. #以及空映射(如空字典)都被视为假,而其他各种值都被视为真,包括特殊值True。
  5. >>> True
  6. True
  7. >>> False
  8. False
  9. >>> True == 1
  10. True
  11. >>> False == 0
  12. True
  13. >>> True + False + 42
  14. 43
  15. #布尔值True和False属于类型bool,而bool与list、str和tuple一样,可用来转换其他的值。
  16. >>> bool('I think, therefore I am')
  17. True
  18. >>> bool(42)
  19. True
  20. >>> bool('')
  21. False
  22. >>> bool(0)
  23. False

分支结构

看官方案例:

  1. >>> x = int(input("Please enter an integer: "))
  2. Please enter an integer: 42
  3. >>> if x < 0:
  4. # 这意味着如果条件(if和冒号之间的表达式)为真,就执行后续代码块;如果条件为假,就不执行
  5. ... x = 0
  6. ... print('Negative changed to zero')
  7. ... elif x == 0:
  8. ... print('Zero')
  9. ... elif x == 1:
  10. ... print('Single')
  11. ... else:
  12. ... print('More')
  13. ...
  14. More

可以有零个或多个 elif 部分,以及一个可选的 else 部分。 关键字’elif’ 是 ‘else if’ 的缩写,适合用于避免过多的缩进。

if嵌套

  1. name = input('请输入名字:')
  2. if name == 'meet':
  3. age = input('请输入年龄:')
  4. if int(age) == 18:
  5. print('输入全部正确')
  6. else:
  7. print('年龄输入错误!')
  8. else:
  9. print('名字输入错误!')

这样写法很常用,这样我们就可以明确的知道用户输入的名字和年龄那个是错误的,并且还做一个判断如果名字正确后再进行密码判断。

更复杂的条件

如何写更复杂的条件,因为它们是if语句执行的重要部分。

1. 比较运算符

在条件表达式中,最基本的运算符可能是比较运算符,它们用于执行比较。

表达式 描述
x == y x等于y
x < y x小于y
x > y x大于y
x >= y x大于或等于y
x <= y x小于或等于y
x != y x不等于y
x is y x和y是同一个对象
x is not y x和y是不同的对象
x in y x是容器(如序列)y的成员
x not in y x不是容器(如序列)y的成员
  1. #要确定两个对象是否相等,可使用比较运算符,用两个等号(==)表示。
  2. >>> "foo" == "foo"
  3. True
  4. >>> "foo" == "bar"
  5. False
  6. #is检查两个对象是否相同(而不是相等)。
  7. >>> x = [1,2,3]
  8. >>> y = [1,2,3]
  9. >>> x == y
  10. True
  11. >>> x is y
  12. False
  13. #不要将is用于数和字符串等不可变的基本值。鉴于Python在内部处理这些对象的方式,
  14. #这样做的结果是不可预测的。
  15. #in:成员资格运算符
  16. name = input('What is your name?')
  17. if 's' in name:
  18. print('Your name contains the letter "s".')
  19. else:
  20. print('Your name does not contain the letter "s".')
  21. #字符串是根据字符的字母排列顺序进行比较的。
  22. #虽然基于的是字母排列顺序,但字母都是Unicode字符,它们是按码点排列的。
  23. #可以使用ord()函数查看,chr()函数与其相反。
  24. >>> "alpha" < "beta"
  25. True
  26. #其他序列的比较方式与此相同,但这些序列包含的元素可能不是字符,而是其他类型的值。
  27. >>> [1, 2] < [2, 1]
  28. True
  29. >>> [2, [1, 4]] < [2, [1, 5]]
  30. True

2. 布尔运算符

布尔运算符有:and、or和not,通过使用这三个运算符,能以任何方式组合多个条件。

  1. number = int(input('Enter a number between 1 and 10: '))
  2. if number <= 10 and number >= 1:
  3. print('Great!')
  4. else:
  5. print('Wrong!')

循环结构

while循环

基本结构

  1. while 条件:
  2. # 循环体
  3. # 如果条件为真,那么循环则执行
  4. # 如果条件为假,那么循环不执行
  5. # 每次循环完毕都会判断一次条件,条件为True则进入循环,条件为False则退出循环
  6. # 打印1-100的数字
  7. x = 1
  8. while x <= 100:
  9. print(x)
  10. x += 1

条件如果为真就会一直执行下去,也就人们常说的死循环,我们怎么能够让程序停止运行?

刚刚说到,死循环是因为条件一直都为真的时候,如果想让while循环停止最简单的方式就是将条件修改成假的,看下面示例

  1. flage = True
  2. str_num = input("请输入要比较的数字:")
  3. print("进入循环")
  4. while flage:
  5. if "3" > str_num:
  6. print("在执行循环")
  7. else:
  8. print("要终止循环")
  9. flage = False
  10. print("退出循环")

我们现在知道可以通过变量的形式改变while循环,我们还可以通过计数的方式来控制循环执行循环的次数:

  1. count = 0
  2. while count < 100:
  3. count = count + 1
  4. print(count)

for循环

为序列(或其他可迭代对象)中每个元素执行代码块,可以使用for循环。

  1. words = ['this', 'is', 'an', 'ex', 'parrot']
  2. for word in words:
  3. print(word)
  4. #或
  5. numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  6. for number in numbers:
  7. print(number)

注意:只要能够使用for循环,就不要使用while循环。

迭代字典

对字典进行迭代

  1. #要遍历字典的所有关键字,可像遍历序列那样使用普通的for语句。
  2. d = {'x': 1, 'y': 2, 'z': 3}
  3. for key in d:
  4. print(key, 'corresponds to', d[key])
  5. #也可使用keys等字典方法来获取所有的键。
  6. #如果只对值感兴趣,可使用d.values
  7. #d.items以元组的方式返回键值对。
  8. #for循环的优点之一是,可在其中使用序列解包。
  9. for key, value in d.items():
  10. print(key, 'corresponds to', value)

并行迭代

同时迭代两个序列

  1. #如何同时迭代两个序列
  2. names = ['anne', 'beth', 'george', 'damon']
  3. ages = [12, 45, 32, 102]
  4. for i in range(len(names)):
  5. print(names[i], 'is', ages[i], 'years old')

是内置函数zip,将两个序列“缝合”起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看其内容,可使用list将其转换为列表。

  1. #zip函数使用
  2. >>> zip(names,ages)
  3. <zip object at 0x7fda73e07048>
  4. >>> list(zip(names,ages))
  5. [('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
  6. #“缝合”后,可在循环中将元组解包
  7. >>> for name,age in zip(names,ages):
  8. ... print(name,'is',age,'years old')
  9. ...
  10. anne is 12 years old
  11. beth is 45 years old
  12. george is 32 years old
  13. damon is 102 years old
  14. #函数zip可用于“缝合”任意数量的序列。当序列的长度不同时,函数zip将
  15. #在最短的序列用完后停止“缝合”。
  16. >>> list(zip(range(5), range(100000000)))
  17. [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

迭代时获取索引

在有些情况下,你需要在迭代对象序列的同时获取当前对象的索引。例如,你可能想替换一个字符串列表中所有包含子串’xxx’的字符串。

  1. >>> list1 = ['功夫之王','电影之最','最佳电影','功夫泰斗','电影总统山']
  2. >>> index = 0
  3. >>> for str1 in list1:
  4. ... if '电影' in str1:
  5. ... list1[index] = '替换电影'
  6. ... index += 1
  7. ...
  8. >>> list1
  9. ['功夫之王', '替换电影', '替换电影', '功夫泰斗', '替换电影']

另一种解决方案是使用内置函数enumerate。这个函数让你能够迭代索引–值对,其中的索引是自动提供的。

  1. >>> list1 = ['功夫之王','电影之最','最佳电影','功夫泰斗','电影总统山']
  2. >>> print(enumerate(list1))
  3. <enumerate object at 0x7fda73e04dc8>
  4. >>> for i in enumerate(list1):
  5. ... print(i)
  6. ...
  7. (0, '功夫之王')
  8. (1, '电影之最')
  9. (2, '最佳电影')
  10. (3, '功夫泰斗')
  11. (4, '电影总统山')
  12. >>> list1 = ['功夫之王','电影之最','最佳电影','功夫泰斗','电影总统山']
  13. >>> for Index,String in enumerate(list1):
  14. ... if '电影' in String:
  15. ... list1[Index] = '替换电影'
  16. ...
  17. >>> list1
  18. ['功夫之王', '替换电影', '替换电影', '功夫泰斗', '替换电影']

反向迭代和排序后再迭代

两个很有用的函数:reversed和sorted。它们类似于列表方法reverse和sort(sorted接受的参数也与sort类似),但可用于任何序列或可迭代的对象,且不就地修改对象,而是返回反转和排序后的版本。

  1. #请注意,sorted返回一个列表,而reversed像zip那样返回一个更神秘的可迭代对象。
  2. >>> sorted([4, 3, 6, 8, 3])
  3. [3, 3, 4, 6, 8]
  4. >>> sorted('Hello, world!')
  5. [' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
  6. >>> list(reversed('Hello, world!'))
  7. ['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
  8. >>> ''.join(reversed('Hello, world!'))
  9. '!dlrow ,olleH'
  10. #要按字母表排序,可先转换为小写。为此,可将sort或sorted的key参数设置为str.lower。
  11. >>> sorted('ANmdhaijD',key=str.lower)
  12. ['A', 'a', 'd', 'D', 'h', 'i', 'j', 'm', 'N']
  13. #使用实例
  14. >>> list1 = ['功夫之王','电影之最','最佳电影','功夫泰斗','电影总统山']
  15. >>> for i in reversed(list1):
  16. ... print(i)
  17. ...
  18. 电影总统山
  19. 功夫泰斗
  20. 最佳电影
  21. 电影之最
  22. 功夫之王
  23. >>> for i in sorted(list1):
  24. ... print(i)
  25. ...
  26. 功夫之王
  27. 功夫泰斗
  28. 最佳电影
  29. 电影之最
  30. 电影总统山

跳出循环

通常,循环会不断地执行代码块,直到条件为假或使用完序列中的所有元素。但在有些情况下,你可能想中断循环、开始新迭代(进入“下一轮”代码块执行流程)或直接结束循环。

1. break

要结束(跳出)循环,可使用break。

  1. # 猜数游戏
  2. import random
  3. answer = random.randint(1, 100)
  4. counter = 0
  5. while True:
  6. counter += 1
  7. number = int(input('请输入: '))
  8. if number < answer:
  9. print('大一点')
  10. elif number > answer:
  11. print('小一点')
  12. else:
  13. print('恭喜你猜对了!')
  14. break
  15. print('你总共猜了%d次' % counter)
  16. if counter > 7:
  17. print('你的智商余额明显不足')

需要注意的是break只能终止它所在的那个循环,这一点在使用嵌套的循环结构需要引起注意。

2. continue

语句continue没有break用得多。它结束当前迭代,并跳到下一次迭代开头。这基本上意味着跳过循环体中余下的语句,但不结束循环。这在循环体庞大而复杂,且存在多个要跳过它的原因时很有用。

  1. #打印1-100间的基数
  2. >>> for i in range(1,101):
  3. ... if i % 2 == 0 :
  4. ... continue
  5. ... print(i,end=',')

循环中的 else 子句

通常,在循环中使用break是因为你“发现”了什么或“出现”了什么情况。要在循环提前结束时采取某种措施很容易,但有时候你可能想在循环正常结束时才采取某种措施。如何判断循环是提前结束还是正常结束的呢?可在循环开始前定义一个布尔变量并将其设置为False,再在跳出循环时将其设置为True。这样就可在循环后面使用一条if语句来判断循环是否是提前结束的。

  1. #以下为伪代码,只是为了说明程序逻辑
  2. broke_out = False
  3. for x in seq:
  4. do_something(x)
  5. if condition(x):
  6. broke_out = True
  7. break
  8. do_something_else(x)
  9. if not broke_out:
  10. print("I didn't break out!")

一种更简单的办法是在循环中添加一条else子句,它仅在没有调用break时才执行。

  1. #找出小于100的最大平方值(整数与自己相乘的结果),答案应该是81
  2. from math import sqrt
  3. for n in range(99, 81, -1):
  4. root = sqrt(n)
  5. if root == int(root):
  6. print(n)
  7. break
  8. else:
  9. print("Didn't find it!")
  10. # 程序的结果是打印出"Didn't find it!",因为break没有被执行

无论是在for循环还是while循环中,都可使用continue、break和else子句。

简单推导

列表推导是一种从其他列表创建列表的方式,类似于数学中的集合推导。

  1. #列表推导的工作原理非常简单,有点类似于for循环。
  2. >>> [x * x for x in range(10)]
  3. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  4. # 还可以加上条件
  5. >>> [x * x for x in range(10) if x % 3 == 0]
  6. [0, 9, 36, 81]
  7. #还可添加更多的for部分
  8. >>> [(x, y) for x in range(3) for y in range(3)]
  9. [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

使用圆括号代替方括号并不能实现元组推导,而是将创建生成器

可使用花括号来执行字典推导

  1. >>> squares = {i:"{} squared is {}".format(i, i**2) for i in range(10)}
  2. >>> squares[8]
  3. '8 squared is 64'

pass

什么都不做的时候,可以使用pass语句。为何需要一条什么都不做的语句呢?在你编写代码时,可将其用作占位符。

  1. if name == 'Ralph Auldus Melish':
  2. print('Welcome!')
  3. elif name == 'Enid':
  4. # 还未完成……
  5. pass
  6. elif name == 'Bill Gates':
  7. print('Access Denied')

exec()

exec执行储存在字符串或文件中的Python语句

  1. # 单行语句字符串
  2. >>> exec('print("Hello World")')
  3. Hello World
  4. >>> exec("print ('runoob.com')")
  5. runoob.com
  6. # 多行语句字符串
  7. >>> exec ("""for i in range(5):
  8. ... print ("iter time: %d" % i)
  9. ... """)
  10. iter time: 0
  11. iter time: 1
  12. iter time: 2
  13. iter time: 3
  14. iter time: 4

eval()

eval是一个类似于exec的内置函数。exec执行一系列Python语句,而eval计算用字符串表示的Python表达式的值,并返回结果(exec什么都不返回,因为它本身是条语句)。

  1. >>> x = 7
  2. >>> eval( '3 * x' )
  3. 21
  4. >>> eval('pow(2,2)')
  5. 4
  6. >>> eval('2 + 2')
  7. 4
  8. >>> n=81
  9. >>> eval("n + 4")
  10. 85

eval()函数传进去的参数必须是字符串或者是对象,eval去除引号后会检查到它是不是可计算的,如果可计算会将计算的结果打印出来,如果不可计算直接返回结果。

  1. #不可计算
  2. >>> s = '["a","b","c"]'
  3. >>> print(eval(s))
  4. ['a', 'b', 'c']
  5. #可计算
  6. >>> s = 'abs(-10)'
  7. >>> print(eval(s))
  8. 10
  9. >>> s = '10 * 2'
  10. >>> print(eval(s))
  11. 20