疯狂Python讲义-李刚编著-第4章流程控制

第4章 流程控制

4.1.顺序结构

4.2.if 分支结构

if expression:
    statements...

if expression:
    statements...
else:
    statements...

if expression:
    statements...
elif expression:
    statements...
...
else:
    statements...

4.2.1.不要忘记缩进

Python 通常建议缩进 4 个空格

4.2.2.不要随意缩进

同一个代码块内的代码必须保持相同的缩进,不能一会缩进 2 个空格,一会缩进 4 个空格 

4.2.3.不要遗忘冒号

4.2.4.if 条件的类型

4.2.5.if 分支的逻辑错误

4.2.6.if 表达式

4.2.7.pass 语句

很多程序都提供了"空语句"支持,Python 也不例外,Python 的 pass 语句就是空语句。

有时候程序需要占一个位、放一条语句,但又不希望这条语句做任何事情,此时就可通过 pass 语句来实现。通过使用 pass 语句,可以让程序更完整。

s = input("请输入一个整数:")
s = int(s)
if s > 5:
    print("大于5")
elif s < 5:
    pass
else:
    print("等于5")

4.3.断言

断言语句和 if 分支有点类似,它用于对一个 bool 表达式进行断言,如果该 bool 表达式为 True,该程序可以继续向下执行;否则程序会引发 AssertionError 错误。

s_age = input("请输入您的年龄")
age = int(s_age)
assert 20 < age < 80
print("您输入的年龄在 20 和 80 之间")

assert 断言的执行逻辑是:

if 条件为 false:
    程序引发 AssertionError 错误

4.4.循环结构

4.4.1.while 循环

4.4.2.使用 while 循环遍历列表和元组

4.4.3.for-in 循环

4.4.4.使用 for-in 循环遍历列表和元组

4.4.5.使用 for-in 循环遍历字典

字典包含如下三个方法

1.items():返回字典中所有 key-value 对的列表

2.keys():返回字典中所有 key 的列表

3.values():返回字典中所有 value 的列表

my_dict = {'语文':89, '数学':92, '英文':80}
for key, value in my_dict.items():
    print('key:', key)
    print('value:', value)
print('----------------------')
for key in my_dict.keys():
    print('key:', key)
    print('value:', my_dict[key])
print('----------------------')
for value in my_dict.values():
    print('value:', value)    

4.4.6.循环使用 else

Python 的循环都可以定义 else 代码块,当循环条件为 Flase 时,程序会执行 else 代码块

count_i = 0
while count_i < 5:
    print('count_i 小于 5:', count_i)
    count_i += 1
else:
    print('count_i 大于或等于 5:', count_i)

4.4.7.嵌套循环

4.4.8.for 表达式

for 表达式用于利用其他区间、元组、列表等可迭代对象创建新的列表。for 表达式的语法格式如下:

[表达式 for 循环计数器 in 可迭代对象]

for 表达式与普通 for 循环的区别有两点:

1.在 for 关键字之前定义一个表达式,该表达式通常会包含循环计数器

2.for 表达式没有循环体,因此不需要冒号

for 表达式当然也是有循环的,它同样会对可迭代对象进行循环——可迭代对象包含几个对象,该循环就对 for 之前的 "表达式" 执行几次(相当于 for 之前的表达式就是循环体),并将每次执行的值收集起来作为新的列表元素。

for 表达式最终返回的是列表,因此 for 表示式也被称为列表推导式。

a_range = range(10)
a_list = [x * x for x in a_range]
print(a_list)

b_list = [x * x for x in a_range if x % 2 == 0]
print(b_list)

如果将 for 表达式的方括号改为圆括号,for 表达式将不再生成列表,而是生成一个生成器(generator),该生成器同样可使用 for 循环迭代。

c_generator = (x * x for x in a_range if x % 2 == 0)
for i in c_generator:
    print(i, end='\t')
print()

实际上 for 表达式可使用多个循环,就像嵌套循环一样。

d_list = [(x, y) for x in range(5) for y in range(4)]
print(d_list)

当然,也支持类似于三层嵌套的 for 表达式

e_list = [[x,y,z] for x in range(5) for y in range(4) for z in range(6)]
print(e_list)

对于包含多个循环的 for 表达式,同样可指定 if 条件。假如我们有一个需求:程序要将两个列表中的数值按"能否整除"的关系配对在一起。比如 src_a 列表中包含30,src_b 列表中包含 5,其中 30 可以整除 5,那么就将 30 和 5 配对在一起。

src_a = [30, 12, 66, 34, 39, 78, 36, 57, 121]
src_b = [3, 5, 7, 11]
result = [(x, y) for x in src_b for y in src_a if y % x == 0]
print(result)

4.4.9.常用工具函数

zip():可以把多个列表"压缩"成一个 zip 对象(可迭代对象),这样就可以使用一个循环并行遍历两个列表。

a = ['a', 'b', 'c']
b = [1, 2, 3]
[x for x in zip(a, b)]

#结果
[('a', 1), ('b', 2), ('c', 3)]

reversed():该函数可接收各种序列(元组、列表、区间等)参数,然后返回一个"反序排列"的迭代器,该函数对参数本身不会产生影响。

a = range(10)
[x for x in reversed(a)]

sorted():该函数接收一个可迭代对象作为参数,返回一个对元素排序的列表。sorted() 函数还可传入一个 reverse 参数,如果将该参数设置为 True,则表示反向排序。还可传入一个 key 参数,该参数可指定一个函数来生成排序的关键值。

a = [20, 30, -1.2, 3.5, 90, 3.6]
sorted(a)
print(a)

sorted(a, reverse=True)
print(a)

b = ['fkit', 'crazyit', 'Charlie', 'fox', 'Emily']
sorted(b, key=len)
print(b)

for s in sorted(b, key=len):
    print(s)

4.5.控制循环结构

4.5.1.使用 break 结束循环

4.5.2.使用 continue 忽略本次循环的剩下语句

4.5.3.使用 return 结束方法

4.6.牛刀小试

4.6.1.数字转人民币读法

下面会实现在实际开发中常用的一个工具函数:将一个浮点数转换成人民币读法的字符串,这个程序需要使用数组。实现这个函数的思路是,首先把这个浮点数分成整数部分和小数部分。提取整数部分很容易,直接将这个浮点数强制类型转换成一个整数即可,这个整数就是浮点数的整数部分;再使用浮点数减去整数就可以得到这个浮点数的小数部分。

然后分开处理整数部分和小数部分。小数部分的处理比较简单,直接截取保留 2 位数字,转换成几角几分的字符串。整数部分的处理则稍微复杂一点,但只要认真分析不难发现,中国的数字习惯是 4 位一节的,一个 4 位的数字可被转成几千几百几十几,至于后面添加什么单位则不确定,如果这节 4 位数字出现在 1~4 位,则后面添加单位 "元";如果这节 4 位数字出现在 5~8 位,则后面添加单位 "万";如果这节 4 位数字出现在 9~12 位,则后面添加单位 "亿";多于 12 位就暂不考虑了。

'''
  把一个浮点数分解成整数部分和小数部分字符串
  num 是需要被分解的浮点数
  返回分解出来的整数部分和小数部分
  第一个数组元素是整数部分,第二个数组元素是小数部分
'''
def divide(num):
    #将一个浮点数强制类型转换为 int 类型,即得到它的整数部分
    integer = (int)num
    #浮点数减去整数部分,得到小数部分,小数部分乘以 100 后再取整,得到 2 位小数
    fraction = round((num - integer) * 100)
    #下面把整数转换为字符串
    return (str(integer), str(fraction))

han_list = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"]
unit_list = ["十", "百", "千"]

'''
  把一个 4 位的数字字符串变成汉字字符串
  num_str 是需要被转换的 4 位数字字符串
  返回 4 位数字字符串被转换成汉字字符串 
'''
def four_to_hanstr(num_str):
    result = ""
    num_len = len(num_str)
    #依次遍历数字字符串的每一位数字
    for i in range(num_len):
        #把字符串转换成数值
        num= int(num_str[i])
        #如果不是最后一位数字,而且数字不是零,则需要添加单位(千、百、十)
        if i != num_len - 1 and num != 0:
            result += han_list[num] + unit_list[num_len - 2 - i]
        #否则不要添加单位
        else:
            result += han_list[num]

'''
  把数字字符串变成汉字字符串
  num_str 是需要被转换的数字字符串
  返回数字字符串被转换成汉字字符串
''' 
def integer_to_str(num_str):
    str_len = len(num_str)
    if str_len > 12:
        print("数字太大,翻译不了")
        return
    #如果大于 8 位,包含单位 "亿"
    elif str_len > 8:
        return four_to_hanstr(num_str[:-8]) + "亿" + four_to_hanstr(num_str[-8:-4]) + "万" + four_to_hanstr(num_str[-4:])
    #如果大于 4 位,包含单位 "万"
    elif str_len > 4:
        return four_to_hanstr(num_str[:-4]) + "万" + four_to_hanstr(num_str[-4:])
    else:
        return four_to_hanstr(num_str)

num = float(input("请输入一个浮点数:"))
#测试把一个浮点数分解成整数部分和小数部分
integer, fraction = divide(num)
#测试把一个 4 位的数字字符串变成汉字字符串
print(integer_to_str(integer))
print(fraction)

从上面的程序的运行结果看,初步实现了所需功能,但这个程序并不是这么简单的,对零的处理比较复杂。例如两个零连在一起时该如何处理呢?还有小数部分如何翻译?因此,这个程序还需要继续完善。

4.6.2.绕圈圈

下面是来自某知名公司的一道"面试题"。

给定 4,应该输出如下形式的数据。

01 12 11 10
02 13 16 09
03 14 15 08
04 05 06 07

给定 5,应该输出如下形式的数据。

01 16 15 14 13
02 17 24 23 12
03 18 25 22 11
04 19 20 21 10
05 06 07 08 09
...

掌握上面的规律之后,我们打算使用列表嵌套列表(相当于二维列表)的方式来存储这些整数,将数值存入嵌套列表时需要遵守这种"绕圈圈"的规则,然后再以二维方式将这个嵌套列表打印出来。

为了控制"绕圈",该程序的关键点就是控制绕圈的拐弯点。

规则如下

1.位于 1 号转弯线的行索引与列索引总和为 n-1(即给定整数值减 1)

2.位于 2 号转弯线的行索引与列索引相等

3.位于 3 号转弯线的行索引等于列索引减 1

SIZE = 7
array = [[0] * SIZE]
#创建一个长度为 SIZE * SIZE 的二维列表
for i in range(SIZE - 1):
    array += [[0] * SIZE]
#该 orient 代表绕圈的方向
#其中 0 代表向下,1 代表向右,2 代表向左,3 代表向上
orient = 0
#控制将 1~SZIE * SIZE 的数值填入二位列表中
#其中 j 控制行索引,k 控制列索引
j = 0
k = 0
for i in range(1, SIZE * SIZE + 1):
    array[j][k] = i
    #如果位于图4.2中①号转弯线上
    if j + k == SIZE - 1:
        #j>k,位于左下角
        if j > k:
            orient = 1
        #位于右上角
        else:
            orient = 2  
    #如果位于图4.2中②号转弯线上
    elif (k == j) and (k >= SIZE / 2):
        orient = 3
    #如果位于图4.2中③号转弯线上 
    elif (j == k - 1) and (k <= SIZE / 2):
        orient = 0
    #根据方向来控制行索引、列索引的改变
    #如果方向向下绕圈
    if orient == 0:
        j += 1
    #如果方向向右绕圈
    elif orient == 1:
        k += 1
    #如果方向向左绕圈
    elif orient == 2:
        k -= 1
    #如果方向向上绕圈
    elif orient == 3:
        j -= 1
#采用遍历输出上面的二维列表
for i in range(SIZE):
    for j in range(SIZE):
        print('%02d ' % array[i][j], end = "")
    print("")     

上面程序的重点就在于粗体字代码,这些粗体字代码控制当处于转弯线上时绕圈的方向。一旦正确控制了绕圈的方向,接下来就可通过对 j(行索引)、k(列索引)的增减来控制绕圈了。运行结果如下:

01 24 23 22 21 20 19
02 25 40 39 38 37 18 
03 26 41 48 47 36 17
04 27 42 49 46 35 16
05 28 43 44 45 34 15
06 29 30 31 32 33 14
07 08 09 10 11 12 13

4.6.3.控制台五子棋

利用二位列表还可以完成五子棋、连连看、俄罗斯方块、扫雷等常见小游戏。

下面利用二维列表实现五子棋。

先定义一个二维列表作为棋盘,每当一个棋手下一步棋时,也就是为二维列表的一个数组元素赋值。

#定义棋盘的大小
BOARD_SIZE = 15
#定义一个二维列表来充当棋盘
board = []
def initBoard():
    #为每个元素赋值"+",用于在控制台画出棋盘
    for i in range(BOARD_SIZE):
        row = ["+"] * BOARD_SIZE
        board.append(row)
#在控制台输出棋盘的方法
def printBoard():
    #打印每个列表元素
    for i in range(BOARD_SIZE):
        for j in range(BOARD_SIZE):
            #打印列表元素后不换行
            print(board[i][j], end="")
        #每打印完一行列表元素后输出一个换行符
        print()
initBoard()
printBoard()
inputStr = input("请输入您下棋的坐标,应以 x,y 的格式:\n")
while inputStr != None:
    #将用户输入的字符串以逗号(,)作为分隔符,分隔成两个字符串
    x_str, y_str = inputStr.split(sep = ",")
    #为对应的列表赋值"•"
    board[int(x_str) - 1][int(y_str) - 1] = "•"
    '''
      电脑随机生成两个整数,作为电脑下棋的坐标,赋值给 board 列表
      还涉及
          1.坐标的有效性,只能是数字,不能超出棋盘范围
          2.下的棋的点,不能重复下棋
          3.每次下棋后,需要扫描谁赢了    
    '''
    printBoard()
    inputStr = input("请输入您下棋的坐标,应以 x,y 的格式:\n")

程序可以增加人工智能。

需要在这个程序的基础上进行完善,保证在用户和电脑下的棋的坐标上不能有棋子(通过判断对应数组元素只能是 "+" 来确定),还需要进行 4 次循环扫描,判断横、竖、左斜、右斜是否有 5 颗棋子连在一起,从而判定胜负。 

4.6.4.控制台超市系统

用户可通过程序提供的命令进行购物

该程序的主要目的就是练习列表、字典等各种数据结构的用法,因此程序会用到如下数据

1.程序使用元组代表商品,元组的多个元素分别代表商品条码、商品名称、商品单价

2.程序使用 dict 来表示系统当前仓库中的所有商品,dict 的 key 是商品条码,value 是商品元组

3.程序使用 list 列表来记录用户的购物清单,list 列表的元素代表购物明细项,每个明细项也是一个 list 表

程序提供了如下功能

1.显示当前超市的商品清单:遍历代表仓库的 dict 中的 values() 返回值,即可显示当前超市的商品清单

2.显示用户的购物清单:遍历代表用户购物清单的 list 列表,即可显示用户的购物清单

3.用户添加购买的商品:向代表用户购物清单的 list 列表中添加一项

4.用户修改购买的商品的数量:修改代表用户购物清单的 list 列表的元素

5.用户删除已购买的商品:删除代表用户购物清单的 list 列表的元素

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 定义仓库
repository = dict()
# 定义购物清单对象
shop_list = []
#定义一个函数来初始化商品
def init_repository():
    # 初始化很多商品,每个元组代表一个商品
    goods1 = ("1000001", "疯狂 Ruby 讲义", 88.0)
    goods2 = ("1000002", "疯狂 Swift 讲义", 69.0)
    goods3 = ("1000003", "疯狂 Kotlin 讲义", 59.0)
    goods4 = ("1000004", "疯狂 Java 讲义", 109.0)
    goods5 = ("1000005", "疯狂 Android 讲义", 108.0)
    goods6 = ("1000006", "疯狂 IOS 讲义", 77.0)
    # 把商品上库(放入 dict 中),条码作为 key
    repository[goods1[0]] = goods1
    repository[goods2[0]] = goods2
    repository[goods3[0]] = goods3
    repository[goods4[0]] = goods4
    repository[goods5[0]] = goods5
    repository[goods6[0]] = goods6
# 显示超市的商品清单,就是遍历代表仓库的 dict 字典
def show_goods():
    print("欢迎光临 疯狂超市")
    print("疯狂超市的商品清单:")
    print("%13s%40s%10s" % ("条码", "商品名称", "单价"))
    # 遍历 repository 中的所有 value 来显示商品清单
    for goods in repository.values():
        print("%15s%40s%12s" % goods)
# 显示购物清单,就是遍历代表购物清单的 list 列表
def show_list():
    print("=" * 100)
    # 如果清单不为空,则输出清单的内容
    if not shop_list:
        print("还未购买商品")
    else:
        title = "%-5s|%15s|%40s|%10s|%4s|%10s" % ("ID", "条码", "商品名称", "单价", "数量", "小计")
        print(title)
        print("-" * 100)
        # 记录总计的价钱
        sum = 0
        # 遍历代表购物清单的 list 列表
        for i, item in enumerate(shop_list):
            # 转换 id 为索引加 1
            id = i + 1
            # 获取该购物明细项的第1个元素:商品条码
            code = item[0]
            # 获取商品条码读取商品,再获取商品名称
            name = repository[code][1]
            # 获取商品条码读取商品,再获取商品单价
            price = repository[code][2]
            # 获取该购物明细项的第2个元素:商品数量
            number = item[1]
            # 小计
            amount = price * number
            # 计算总计
            sum = sum + amount
            line = "%-5s|%17s|%40s|%12s|%6s|%12s" % (id, code, name, price, number, amount)
            print(line)
        print("-" * 100)
        print("                                  总计:", sum)
    print("-" * 100)
# 添加购买的商品,就是向代表用户购物清单的 list 列表中添加一项
def add():
    # 等待输入条码
    code = input("请输入商品的条码:\n")
    # 没有找到对应的商品,条码错误
    if code not in repository:
        print("条码错误,请重新输入")
        return
    # 根据条码找商品
    goods = repository[code]
    # 等待输入数量
    number = input("请输入购买数量:\n")
    # 把商品和购买数量封装成 list 后加入购物清单中
    shop_list.append([code, int(number)])
# 修改购物商品的数量,就是修改代表用户购物清单的 list 列表的元素
def edit():
    id = input("请输入要修改的购物明细项的 ID:\n")
    # id 减 1 得到购物明细项的索引
    index = int(id) - 1
    # 根据索引获取某个购物明细项
    item = shop_list[index]
    # 提示输入新的购买数量
    number = input("请输入新的购买数量:\n")
    # 修改 item 里面的 number
    item[1] = int(number)
# 删除已购买的商品明细项,就是删除代表用户购物清单的 list 列表的元素
def delete():
    id = input("请输入要删除的购物明细项的 ID:")
    index = int(id) - 1
    # 直接根据索引从清单里面删除购物明细项
    del shop_list[index]
def payment():
    # 先打印清单
    show_list()
    print('\n' * 3)
    print("欢迎下次光临")
    # 退出程序
    import os
    os._exit(0)
cmd_dict = {'a': add, 'e': edit, 'd': delete, 'p': payment, 's': show_goods}
# 显示命令提示
def show_command():
    # 等待命令
    cmd = input("请输入操作指令:\n" + "   添加(a)   修改(e)   删除(d)   结算(p)   超市商品(s)\n")
    # 如果用户输入的字符没有对应的命令
    if cmd not in cmd_dict:
        print("不要玩,好不好!")
    else:
        cmd_dict[cmd]()
init_repository()
show_goods()
# 显示清单和操作命令提示
while True:
    show_list()
    show_command()

4.7.总结

posted on 2020-01-21 08:47  herisson_pan  阅读(13)  评论(0)    收藏  举报

导航