4.更多的控制流工具 编译之四

标题 4更多的控制流工具(1) 编译之四

除了刚才引介的while陈述,python通常使用其它语言一些熟知的流控制陈述,伴有一些变化。

标题4.1.如果…那么if 语句

也许,最为著名的语句类型就是这个if语句了。例如:
学习python,这里就开始折磨人了,虽然如导引所言,python容易学,但许多细节得有时间消化牢记,不然,这编辑器的知识掌握就几乎不可能。看起来简单的一个代码输入,许多细节一点也不能马虎,否则你就动弹不得。
简单举几个细节:
首行末尾不用双括号出现无效字符,缩略行对齐有问题,也是无效字符。单引号和双引号也得注意细节。中英文切换不注意,也出现无效字符问题。注意那个if后的语句一定要带冒号:,不然也是无效字符。几乎每走一步都是坑,你只有格外小心才不会出现阻挡你前进的坑坑洼洼。
那个编译器的把握也是个问题,你cope代码时总要用那个左上角的小小图标,很容易就退出来,所有的代码就会消失,还没有找到好办法解决,只有小心翼翼地不让它退出,但经常失败,让人揪心。
If代码输入:

在这里插入代码片>>> x = int(input("Please enter an integer:"))
Please enter an integer:42
>>> if x < 0:
...    x = 0
...    print('Nrgative changed to zero')
... elif x == 0:
...    print('Zero')
... elif x == 1:
...    print('Single')
... else:
...    print('More')
...
More
>>>

自然就很可能有零或者比零大的更多elif部分,那个else部分则是可选的。关键字‘elif是英文else if的缩写,中文是“否则”的含义,它常用来避免过度缩进。一个if,elif和else组成的代码序列在其它语言中被switch和case陈述所替代。
以下再给出一个在笨办法学python中用过的if序列:

在这里插入代码片if cars > people:
    print("We should take the cars.")
elif cars < people:
    print("We should not take the cars.")
else:
    print("We can't decide.")

if trucks > cars:
    print("That's too many trucks.")
elif trucks < cars:
    print("Maybe we could take the trucks.")
else:
    print("We still can't decide.")

if people > trucks:
    print("Alright, let's just take the trucks.")
else:
    print("Fine, let's stay home then.")

标题4.2. 让对象循环的for 语句

python中的for语句与我们在C语言或者Pascal语言用到的东西有一些区别。它并不是总是迭代一个连续数字,如同Pascal那样,或者让用户自己去决定迭代的步骤和迭代的条件,如同C语言那样。python的for语句迭代任意系列(列表或者字符串)中的项目,这些项目处在序列中出现的秩序之中。例如(无双关意图):

在这里插入代码片>>> #测试某些字符串

测试某些字符串:

words = [‘cat’, ‘window’, ‘defenestrate’]
for w in words:
… print(w, len(w))

cat 3
window 6
defenestrate 12

当我们迭代同样的一个集合,我们在为这个集合予以修改重新编码以获得一个新复本,这样的编码可以用一个聪明的方式获得。通常是更为直接地循环一个该集合的复本,或者去重新创建一个新集合。
这个迭代的用法还没有完全明白,第一个策略大约是弄一个复制本,但为何把用户给删掉呢?第二个大概是重建一个新集合,但这个集合好像是一个词典。花括号不就是词典的标记么?

在这里插入代码片>>> #strategy: Iterage over a copy
>>> for user, status in users.copy().items():
...     if status == 'inactive':
...         del users[user]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'users' is not defined
>>> for user, status in users.copy().items():
...     if status == 'inactive':
...         del users[user]
... active_users = {}
  File "<stdin>", line 4
    active_users = {}
    ^
SyntaxError: invalid syntax
>>> active_users = {}
>>> for user, status in users.items():
...     if status == 'active':
...         active_users[user] = status

标题4.3. 范围 range() 函数

如果你需要迭代一个数字序列,内置函数range()用起来很方便,它能够生成算术连续系列:

在这里插入代码片SyntaxError: invalid syntax
>>> for i in range(7):
...     print(i)
...
0
1
2
3
4
5
6
>>>

那个已知的末端字符不会是生成字符中的一部分,range(10)生成10个数值,长度为10的一个序列中的合法项数就是从零开始的10个数字。有可能让这个range从序列中的任意一个数字开始,或者指定一个不同的间距的数(甚至负数,有时,这被称为步长step)
依据导引的数据打出,结果却不一样,看来这个指令有变化。

在这里插入代码片>>> range(5, 10)
range(5, 10)
>>> range(0, 10, 3)
range(0, 10, 3)
>>> range(-10, -100, -30)
range(-10, -100, -30)
>>>

为了迭代一个序列的索引,可以把range和len结合起来使用。

a = [‘Mary’, ‘had’, ‘a’, ‘little’, ‘lamb’]
for i in range(len(a)):
… print(i a[i])
File “”, line 2
print(i a[i])
^
SyntaxError: invalid syntax

print(i, a[i])

a = [‘Mary’, ‘had’, ‘a’, ‘little’, ‘lamb’]
for i in range((len(a)):
File “”, line 1
for i in range((len(a)):
^
SyntaxError: invalid syntax

for i in range(len(a)):
… print(i, a[i])

0 Mary
1 had
2 a
3 little
4 lamb

然而,在大多数这类情形中,使用例举函数是方便的,可参看循环技巧。
如果你想打印出一个range,会有奇怪的事情发生。我们刚才就发生过这种情形。

在这里插入代码片>>> print(range(10))
range(0, 10)
>>>

在许多情形中,借助range()返回的对象似乎做了列表的行为,但事实上没有做。那是一个返回了所希望的序列的一个连续值,当你迭代它的时候。而实际上这个列表并没有出现,好像是节省了空间似的。
我们说这样一个对象是迭代的,那就是,他作为一个函数目标是合适的,而且建构了那个所期待的东西,这些东西可以获得连续性的项目,直到提供完全部的项目。我们已经看到,一个for语句就正是这样的一个建构,当一个函数的实例作为迭代在做的时候,这个函数可以看作是做加法函数sum():

>>> sum(range(4)) #0+1+2+3
6
>>>

往后,我们将看到更多的返回迭代值的函数,迭代可看作是参数。这一小节的最后,也许你有些奇怪,如何从range函数那里获得一个列表list。这里就是一个答案。

在这里插入代码片>>> list(range(4))
[0, 1, 2, 3]
>>>

在数据库那一章里,我们将更为详细地讨论列表list()函数。

标题4.4. 中断break 和 继续continue 语句, 循环loop中的 else 从句

中断break语句,就像在C语言中一样,是对于循环语句for和while循环过程中间某个位置的一个停顿。
循环语句也许会有某个else子句,如果当这个循环的终端通过耗尽全部迭代(这常常是for循环),或者当那个条件为假(这常常是while循环),但是当循环并未终结,而是因一个中断语句而停止。这种情况可由搜寻整数的下例体现。

在这里插入代码片>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         print(n,'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
>>>

(是的,这是正确的代码,仔细再看:else子句属于for循环,不属于if语句。)
当带有一个循环而用到之时,else子句和try语句中的else子句的共同之处可以和if语句中的情形相比较,前者有更多的共性:一个try语句的else子句多在没有例外发生的时候运行,而一个循环的else子句,则在没有中断break发生的时候运行。有关try语句和例外的更多信息,请看处理例外Handling Exception.
而继续continue语句,也是从C语言借用而来,伴随下一个循环的迭代而继续:

在这里插入代码片
>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9

标题4.5. 通过pass 语句

指令“通过”pass语句什么事情也不做。当一个语句只是从语法上要求,但在程序上要求不去执行什么的时候,这个pass指令就起到这个作用。例如:
The pass statement does nothing. It can be used when a statement is required syntactically but the program requires no action. For example:

while True:
… pass # Busy-wait for keyboard interrupt (Ctrl+C)


这通常用作创建最小的类:

class MyEmptyClass:
… pass

指令pass可以用于另一种情况,即作为一个函数或者条件主体的占位符place-holder而被用到,当你正在执行一个新代码的时候,这个时候就允许你在更为抽象的层次上去思考。这个pass指令有点像静静地暂且忽略:

def initlog(*args):
… pass # Remember to implement this!

标题4.6. 定义Defining 函数

我们还可以创建一个函数,这个函数撰写了一个任意边界的斐波拉契系列:

在这里插入代码片SyntaxError: invalid syntax
>>> def fib(n):
...     """Print a Fibonacci series up to n."""
...     a, b = 0. 1
  File "<stdin>", line 3
    a, b = 0. 1
              ^
SyntaxError: invalid syntax
>>> def fib(n):
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b a+b
  File "<stdin>", line 6
    a, b = b a+b
             ^
SyntaxError: invalid syntax
>>>
>>> def fib(n):
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> #now call the function we just defined:
>>> fib(2500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
>>>

读后感触:
这个斐波拉契函数的代码重写让你在逐渐熟悉这个python语言,缩进很重要,还有一些细节也很重要。def之后是缩进4格,然后使用while循环后,又要缩进4格。而最后的fib的数字要打进去,如果没有那个注释行,会出现语法错误。有点奇怪的是,数字打多了,我打的2500,结果却是一样的。

关键字def引进一个函数定义,这个函数def之后必须跟随函数名function name,再接之以函数的标准参数列表,用圆括号括住。构成函数体function body的语句另起一行,必须缩进四格。
函数体的第一个语句可选为一个字符串文字,这个字符串文字是函数的说明字符,或者是文档字符串docstring。(有关文档字符串的更多内容可在文档字符串document strings中查阅)。有一些工具,它们是用来自动生成在线或者打印的文件的,或者让用户交互式地通过代码浏览:在你撰写的代码中包括文档字符串,这是一个好的实践习惯,你应该去养成这样一种习惯。
一个函数的执行引入了一个新的符号图表,该图表用在函数的局域符号之中。更准确地说,函数中所有的变元指派把值存储在局域符号图表之中,然后再存储在全域符号图表之中,最后则存储在内置名称的图表之中。所以,全域变元和闭包函数的变元不可能在一个函数之内直接指派一个值(对于全域变元来说,除非在一个全域语句中命名过,或者对于闭包函数的变元来说,在一个非局域语句中命名过)虽然它们可能被提及到。
调用函数实际的参数(parameters或者arguments)在局域符号图表中被引入,这个局域符号图表属于那个被调用函数的,所以,用那个值执行调用,参数就被通过pass(这个值总是与对象相关的,但并不是对象的值)。[1]当一个函数调用另一个函数的时候,一个新的局域符号图表就因这个调用而被创建出来。
一个函数定义连接着在当前符号图表中的那个函数对象名称。解释器认出这个对象,那是因为这个对象是用户为函数定义的名称。还有一些别的名称也可以表示同样的函数对象,并且还可以用这些不同名称来登录这个函数:

在这里插入代码片>>> fib
<function fib at 0x000001B205C3AA60>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
>>> f(300)
0 1 1 2 3 5 8 13 21 34 55 89 144 233
>>> f(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>>

若是来自其它语言,你可能对于fib不是一个函数,而是一个演算步骤表示不同意见,因为这个fib并不返回一个值。事实上,没有返回语句的函数是有值返回的,虽然只是一个相当无聊的值,这个值称之为“无”None(这是一个内置函数的名称)。这个值如果只是唯一需要写出的值,在解释器中一般都被略掉了。如果你真的想去使用print():

在这里插入代码片>>> fib(0)

>>> print(fib(0))

None

写出一个函数,一个返回斐波拉契系列数字列表的函数,而不是打印出来,这是很容易的一件事:

在这里插入代码片>>> def fib2(n):
...     """Return a list containing the Fibonacci series up to n."""
...     result = []
...     a, b = 0, 1
...     while a < n:
...         result.append(a)
...         a, b = b, a+b
...     return result
...
>>> f(300) = fib2(300)
  File "<stdin>", line 1
SyntaxError: cannot assign to function call
>>> f300 = fib2(300)
>>> f300
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]

这个实例,如同通常一样,显示python语言的一些新特性:
第一个特性:返回语句用一个来自函数的值,而不是用一个表达式参数的返回语句None来执行返回。
第二个特性:语句result.append(a)调用了列表对象result的一个方法method。这个方法也是一个函数,它是属于一个对象的函数,并且是用对象方法名称来命名的obj.methodname,其中,obj就是某个对象(也可能是一个表达式),那个methodname则是方法的名称,该名称是被对象的类型所定义的。不同的类型定义不同的方法。不同类型的方法也许有同样的名称,只要不造成歧义(使用类class来定义自己的类型和方法那也是可能的,参看类classes)。在实例中显示的方法append()用列表对象来定义,它在列表的末端增加了一个新元素。在这个例子中,这个方法等价于result=result + [a],但比后者更有效。
这个第四章实在太长,分作两个学习编译笔记吧,且把4.7留给下一篇。

猜你喜欢

转载自blog.csdn.net/weixin_41670255/article/details/108275602