1 引入——斐波那契数列
笔者在尝试编写斐波那契数列程序时,看到一个版本的Python实现,代码如下:
n = int(input("需要计算的项数: "))
a = 0
b = 1
for i in range(0,n):
a, b = b, a + b
print(a,end=' ')
在这里给不知道斐波那契数列(Fibonacci sequence)的小伙伴科普一下:
斐波那契数列(Fibonacci sequence),因数学家斐波那契以兔子繁殖为例子而引入,指的是这样的数列:
F 0 = 0 F_0=0 F0=0, F 1 = 1 F_1=1 F1=1, F n = F n − 1 + F n − 2 ( n > 1 ) F_n=F_{n-1}+F_{n-2}\ (n>1) Fn=Fn−1+Fn−2 (n>1)
0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 ⋯ 0,\ 1,\ 1,\ 2,\ 3,\ 5,\ 8,\ 13\ ,21,\ 34,\ 55\cdots 0, 1, 1, 2, 3, 5, 8, 13 ,21, 34, 55⋯
每一个数是前两个数的总和。
这里的赋值语句a, b = b, a + b
实现了同时将a+b赋给b,又将原来的b赋给a的功能。
如果将其拆开的直接写成如下代码的话,结果是错误的
b = a + b
a = b
因为通过第一步b = a + b
,b的值已经发生了改变,而我需要将原来的b赋值给a,所以发生错误,修改:
b = a + b
a = b - a
但是这样修改不如示例代码简洁,所以我决定研究一下Python对于这种赋值语句的原理。
2 表达式列表
经过查阅Python语言参考文档,我找到了这种表示方法,叫做表达式列表 (Expression list)。
文档原文:
6.15. Expression lists
expression_list ::= expression ("," expression)* [","] starred_list ::= starred_item ("," starred_item)* [","] starred_expression ::= expression | (starred_item ",")* [starred_item] starred_item ::= assignment_expression | "*" or_expr
Except when part of a list or set display, an expression list containing at least one comma yields a tuple. The length of the tuple is the number of expressions in the list. The expressions are evaluated from left to right.
原文代码块使用的是一种BNF范式(Backus-Naur form / Backus normal form),是一种描述计算机语法规则的元语言。Python文档使用的是一种修改的BNF范式,在这里简单介绍一下Python使用的BNF范式语法,帮助大家更好的理解Expression List及相关概念。
BNF范式语法:
-
每个规则的开头是规则定义的名称加上
::=
表示:“被定义为” -
竖线
|
用来分隔可选项 -
星号
*
用来表示前一项的零次或多次重复 -
加号
+
用来表示前一项的一次或多次重复 -
方括号括起
[]
表示可以出现零次或一次 -
圆括号用于分组
-
双引号“”括起来的表示括起来的字符本身
介绍完BNF范式语法,看一下expression_list
expression_list ::= expression ("," expression)* [","]
可以证明expression_list就是我们想要的形如a, b
的形式。
文档中提到:除了作为列表或者集合显示的一部分(例:[2, 3]),包含至少一个逗号的表达式列表会生成一个元组,元组长度为列表中表达式的数量,表达式将从左至右进行求值。
通过代码进行验证:
>>> a = 1
>>> b = 2
>>> a , b
(1, 2)
3 赋值语句
看完了表达式列表,接下来看一下赋值语句 (assignment statements).
文档原文:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
表达式列表可以是单一表达式,也可以是逗号分隔的列表(我们着重研究的对象),后者会生成一个元组(这和我们在上文中得到的结论相同),将结果从左到右赋值给目标列表。
对这种赋值方式的详细解释:
Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are ‘simultaneous’ (for example
a, b = b, a
swaps two variables), overlaps within the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion.
赋值语句看起来像是右边所有元素同时赋给左边,但实际上赋值是从左至右的。
文档中给出的例子:
x = [0, 1]
i = 0
i, x[i] = 1, 2 # i is updated, then x[i] is updated
print(x)
右侧是一个表达式列表1, 2
,赋值给左侧,按照从左至右的顺序进行赋值,
首先将1赋给i,其次将2赋给x[i],因为此时i=1,所以x[1]等于2,所以最终结果为[0, 2]
。
解释斐波那契数列的例子:
n = int(input("需要计算的项数: "))
a = 0
b = 1
for i in range(0,n):
a, b = b, a + b
print(a,end=' ')
以第一次循环为例,a = 0, b = 1
,右侧的b, a + b
是一个表达式列表,从左到右进行求值并生成一个元组(1, 1)
,此时右侧的元组中储存的是两个数值,与变量名无关。
按顺序将元组第一个值赋给a
,第二个值赋给b
,完成赋值语句。
通过这样的赋值语句,成功将原来的b
赋给a,将a + b
赋给b
,而没有发生冲突。
本文是本人通过查找阅读Python文档得出的结论,如有不正之处,欢迎大家批评指正。