9. Python程序流程-高级篇

实际上python的流程控制语句和各种变形远不止我们上一章说的那几种,这一章我们将常见的流程控制语句都介绍一下。

如果对生涩理论不感兴趣的同学可以跳过这一章,继续玩硬件~

9.1. for循环

我们前面接触了while的循环,但是平时编写程序有一种更常用的循环--FOR循环,我们先用图形化积木生成一个样板代码:

生成的代码如下:

#/bin/python
from microbit import *

x = 0

for count in range(10):
    display.scroll(x)
    x += 1

但是这时候不要直接下载,因为display的scroll函数不支持数字类型,我们要把它变成字符串。还记得我们之前的str函数吗?

display.scroll(x)改造成display.scroll(str(x))下载就行了,我们可以看到矩阵屏不停飘过数字,最终到9停止。

我们来看看条件表达式for count in range(10):,他的关键字形式是for A in B:。它的作用是对数组B内所有的元素进行循环操作,而A表示就是当前循环的数组元素,一旦循环完成则退出循环。

Range函数

那么range(10)的作用呢?我们来做一个实验,关掉kittenblock的自动代码转换,在代码框写入如下测试代码:

a = range(10)
print(a)
print(type(a))

下载后看看输出:

range(0, 10)

<class 'range'>

我们之前说for A in B:中的B应该是一个数组是不,但是这里打印的range的类型是一个range对象。实际上range函数并不是直接返回一个数组,而是一个叫做迭代器的东西。迭代器允许你对一个对象进行循环操作,数组对象list也隐含了类似的迭代器。实际上迭代器在python解释器中是一个预分配内存的过程,他只在使用的时候才真正的被调用到。如果觉得太抽象,前期可以把它们视作等同的东西,基本上在数组上的操作在range对象上也成立。

我们可以使用list将range变成一个我们真正摸得着看得到的数组:

a = range(10)
b = list(a)
print(b)
print(type(b))

输出如下:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

<class 'list'>

注意这里的对象b是真真切切的占用了我们microbit中10个数字的内存。同理我们使用list作为for循环的操作也是成立的,我们将前面的程序改造成如下:

from microbit import *

a = range(10)
b = list(a)

for x in b:
    display.scroll(str(x))

这里我们去掉了外部变量x,而动态地从数组b中抽取元素,他的名字还是x。

range函数还支持各种花式初始化的方法,大家可以自己试试下面这几种初始化后的range函数返回的数组是怎么样的:

range(10)
range(10, 20)
range(1, 20, 2)
range(1, -20, -1)

9.2. if ... else ...

我们前面用了if来判断按键是否按下,实际上if的变形是python条件控制语句中最灵活的。我们可以打开kittenblock内置的示例程序“AB按键测试 ”

看看自动生成的代码中核心循环控制部分:

while True:
    if button_a.is_pressed() and button_b.is_pressed():
        display.scroll("AB")
    else:
        if button_a.is_pressed():
            display.show(Image("00900:09090:90009:99999:90009"))
        else:
            if button_b.is_pressed():
                display.show(Image("99900:90090:99900:90090:99900"))
            else:
                display.show(Image("90009:09090:00900:09090:90009"))

else的作用域指的就是当if条件不成立所执行的代码块。上面的代码感觉很臃肿,貌似不符合python简洁的特性。毕竟机器生成的代码肯定不如人写的简洁有没,那么我们是否能优化一下。

while True:
    if button_a.is_pressed() and button_b.is_pressed():
        display.scroll("AB")
    elif button_a.is_pressed():
        display.show(Image("00900:09090:90009:99999:90009"))
    elif button_b.is_pressed():
        display.show(Image("99900:90090:99900:90090:99900"))
    else:
        display.show(Image("90009:09090:00900:09090:90009"))

将上面的代码替换掉自动生成的部分,下载到microbit上看看是否效果一样?

我们这里接触到新的关键字elif,实际上跟之前的代码对比一下很容易就明白了其意思。它在前一级if判断不成的情况下,重新进行新的if判断。如果前面所有的if条件判断都不成立,则执行else的部分。

实际上上这种写法在实际研发中相当常见,假设有一个用户要登录你写的服务器。你的服务器代码要检查:

  • 他是不是已经登录过了
  • 他提供的密码是不是正确
  • 他的账号是不是已经被管理员封停了

等等类似的条件判断,每一种判断都有不同的程序执行流程。

9.4. 条件判断的混合用法

大家可能看到上面if button_a.is_pressed() and button_b.is_pressed():中条件判断,实际上不管if后面的条件判断语句有多复杂最终只返回一个真假值(True或者False),如果返回True则执行if下面的代码块,否则则进入下一个elif或者else判断。

我们可以看到这个条件判断的原型是:

当然我们完全可以自己写一个类似的判断:

你会发现生成的代码是一模一样的,这是因为代码生成器做了整合。

如果我想让A或B按键其中一个按下就执行一段程序呢?

请注意if button_a.is_pressed() or button_b.is_pressed():if button_a.is_pressed() and button_b.is_pressed():中间仅仅只是一个单词的差别却具有完全不一样的意义,大家写程序的时候要特别小心,即使很有经验的工程师也常常在条件判断地方栽跟头。

大家可以多试试在kittenblock拖不同的方块出来组合,看看生成额条件判断代码有什么不同。

9.4. 课后作业

还记得我们之前用过的Image的时钟数组吗,现在请试试把它做一个循环的动画(使用循环)