for循环与列表的遍历
先写一个简单的列表循环遍历代码,这样说明比较直观与方便些:
names = [ 'ZhangSan', 'LiSi', 'WangWu' ]
for name in names:
print(name)
这段代码结果显示:
ZhangSan
LiSi
WangWu
这段代码就是一个简单的链表循环遍历,下面我逐一说明下:
第一行定义了 一个名为names的列表,里面有三个元素分别为 ‘ZhangSan’, ‘LiSi’, ‘WangWu’。
第二行是一个for循环用来遍历列表names里的元素,for循环的基本格式是:
for 变量 in 可迭代的集合 :
循环语句1
循环语句2
.
.
.
循环语句n
for循环的行为是在每次循环时从 可迭代的集合 中取出一个元素赋值给 变量 ,然后下面的循环语句中就可以使用这个变量,最后直到集合中的每一个元素都被遍历了一次之后 循环结束。
第三行就是将变量name(每次循环遍历时从names列表中取出一个元素赋值得来)打印出来。
这里需要注意的是一下几点:
- for循环后的冒号不要忘了,这是告诉编译器后面有语句而且是和从属于这一行的。(问:那就写个for语句,后面什么都不跟行不行? 答:不行,如果想什么都不做那也要跟一个pass语句,否则编译不通过)
- for后面的循环语句都要统一缩进(4个空格或一个tab),python对于格式的缩进要就极其严格,多了少了都不行,因为编译器需要用缩进来判断这个语句到底是属于哪一个代码块。一旦缩进不对要么编译器解析你的代码时报错,要么碰巧编译过了却和你想要的运行顺序不一样。
- 如果后面的代码不是循环体的一部分,那么只要去掉一个tab缩进就好了。在python中你可以用缩进来一眼看出代码的层次结构,这也是python令人喜欢的地方之一,代码读起来结构清晰明了。
while循环
既然写到了for循环那么也把while循环介绍下,这样能对比着看一下,我想介绍完了也就明白为什么列表的循环一般都用for而不是while。
while语句的基本结构为:
while 逻辑判断表达式:
循环语句1
循环语句2
.
.
.
循环语句n
while循环的行为是只要逻辑判断表达式的结果为真,则执行循环语句,直至逻辑判断表达式的结果为假时退出。
那么同样是上面的列表,如果用while遍历该怎么写呢?以下是代码:
names = [ 'ZhangSan', 'LiSi', 'WangWu' ]
names_len = len(names)
index = 0
while index < names_len:
print(names[index])
index += 1
看到了吧,for用3行解决的事儿,while需要6行。那么为什么要有while循环呢?那是因为某些情况下while循环比for用起来能更方便的描述现实情况。比如和用户交互,比如一个游戏程序什么时候结束。一般来说执行循环时有明确循环次数的用for循环,而循环次数是未知时则用while循环。
说到循环还有两个关键词不得不说一下,一个是continue,一个是break,这两个关键词经常能在循环中看到。
- continue: 当程序在循环中执行到continue时,程序立刻从循环的头部开始下一次循环的执行(如此一来continue语句下面的循环语句自然是执行不到了)
- break:当程序在循环中执行到break时,程序跳出循环(后面的循环不再执行)接着执行外部语句
continue例子:
names = [ 'ZhangSan', 'LiSi', 'WangWu' ]
names_len = len(names)
index = 0
while index < names_len:
print(names[index])
index += 1
continue
print(names[index])
这段代码的执行结果和上面的一样,因为程序执行到continue后返回循环起始处接着执行下一次循环,后面的print语句根本执行不到
break例子:
names = [ 'ZhangSan', 'LiSi', 'WangWu' ]
names_len = len(names)
index = 0
while index < names_len:
print(names[index])
index += 1
break
print(names[index])
这段代码的执行结果只有一行 ZhangSan,因为程序执行到break后直接跳出循环,后面的循环以及循环体代码都被略过了。有不少人为死循环的退出就是使用break语句跳出死循环的。
在上面的while循环中我提到了逻辑表达式,这其中经常会用到两个关键词,一个是and 一个是 or(也就是C系列语言中的&& 和 || ):
- and:表示并列关系,当左右两边表达式都为真时结果为真,其他情况结果都为假
- or :表示或者关系,当左右两边表达式都为假时结果为假,其他情况结果都为真
数值列表的创建
有的时候我们需要一个由数值组成的列表,这些数值量大又有一定的规律,有什么办法能帮我们一下子创建出这样的列表吗?
这里就用到了函数range()。
例子:
for num in range(1,5):
print(num)
结果输出1,2,3,4.
可以发现range函数生成的列表是包含开始数字,不包括结尾数字的。
range函数还可以加上第三个参数来指定步长,如:
for num in range(1,5,2):
print(num)
结果输出1,3,
如果发现有一个函数不知道它怎么用可以使用help函数,如:help(range)
列表解析
如果我要的一个数字列表他不是个等差数列怎么办?range函数只能生成等差数列的列表啊。
也是有办法的,这时候就用到列表解析了。
举个例子,如果要生成1到6的立方数列表,那么可以这么写:
nums = [num**3 for num in range(1,7)]
print(nums)
上面代码可以l理解为:
nums = []
for num in range(1,7):
nums.append(num**3)
print(nums)
再复杂的列表解析都可以这么理解和转换:
比如:
nums = [x*y*z for x in range(1,10,2) for y in range(4,12,3) for z in range(3,8,4)]
print(nums)
等同于
nums = []
for x in range(1,10,2):
for y in range(4,12,3):
for z in range(3,8,4):
nums.append(x*y*z)
print(nums)
还没说到if语句就不往里面加了,如果有if语句那也是照样子展开意思就一目了然了(不要关注里面的数字,数字是我随便选的,关注如何展开就好)
数字列表的简单统计计算
- min:返回列表中的最小值
- max:返回列表中的最大值
sum:返回列表中数字的和
例子:
nums = range(1,101)
print(min(nums))
print(max(nums))
print(sum(nums))
结果为:
1
100
5050
列表切片
有的时候我们只想使用列表的一部分那怎么办?
这时候列表的切片功能就能帮上忙了。
例子:
nums = [1,2,3,4,5,6]
nums_slice = nums[0:3]
print(nums)
print(nums_slice)
代码的第二行就是切片操作了,将nums列表从第0号元素到第3号元素(不包括下标为3的元素)进行切片操作,然后赋值给nums_slice。
从这段代码中能看出2个问题:
- [起始下标 : 结尾下标] 的切片操作也是包含前面的数字不包含结尾的数字的(python绝大多数表示一个范围的操作都这样)
- 切片操作不会影响原来的列表
切片操作能正着切也能反着切:
nums = [1,2,3,4,5,6]
nums_slice = nums[-1:-3:-1]
print(nums_slice)
结果输出
[6,5]
你也许看出来了,这切片操作的3个参数和range函数很像,都是起始,结束,步长。代码中起始下标-1是倒数第一个数,-3是倒数第三个数,步长-1,那么根据切片包含前不包含后的特点结果就是6(下标-1的数)和5(下标-2的数)了。
列表的复制
列表复制也是常常会用到的一个需求,那么列表该如何复制呢?
先看看这样行不行:
nums_1 = [1,2,3]
nums_2 = [1,2,3]
nums_3 = nums_1
print(nums_1)
print(nums_2)
print(nums_3)
nums_1[0] = 6
print(nums_1)
print(nums_2)
print(nums_3)
print(id(nums_1))
print(id(nums_2))
print(id(nums_3))
打印结果为:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[6, 2, 3]
[1, 2, 3]
[6, 2, 3]
140332352455496
140332352583432
140332352455496
这段代码说明了几个问题:
- 直接用列表名赋值给另一个变量这不是真正的复制列表,只是把地址给了它,导致两个不同名字的变量指向同一个地址,结果自然是改其中一个另一个看起来也改了。
- 指向相同内存地址的变量具有相同的id(id这个函数可以获得一个变量的唯一身份编码),我们可以用id这个函数来识别变量是否指向同一个地址
- 用同样的数组元素复制给变量,不会导致两个数组指向同一地址
(这有点像C里面的指针和地址概念的感觉)
那我们该如何复制呢,总不能每次都手动输入一次吧,数量大了怎么办?这里有两个方法可供选择:
nums_1 = [1,2,3]
nums_2 = nums_1[:] #这种切片中什么都不写的方式 表示切整个列表
nums_3 = nums_1.copy()
print(id(nums_1))
print(id(nums_2))
print(id(nums_3))
输出显示:
140332352585672
140332353006536
140332352545096
id打印结果表明用切片和copy()函数复制列表是可以达成要求的。
需要注意的是这两种方式都是浅拷贝,如果遇到以下这种列表里包含列表的情况就抓瞎了:
nums_1 = [1,2,3,[4,5,6]]
nums_2 = nums_1[:]
nums_3 = nums_1.copy()
nums_1[1]= 9
nums_1[3][0] = 10
print(nums_1)
print(nums_2)
print(nums_3)
print(id(nums_1))
print(id(nums_2))
print(id(nums_3))
显示输出:
[1, 9, 3, [10, 5, 6]]
[1, 2, 3, [10, 5, 6]]
[1, 2, 3, [10, 5, 6]]
140332352939912
140332353033928
140332352517512
从id上能看出这3个变量确实是3个不同的变量,没有指向同一个地址的情况出现,但nums_1列表中的列表[4,5,6]被改了第一个元素,另外两个变量看起来也被改了。这里面涉及到深拷贝的问题,后面再说。
元组
元组的定义方式为:
变量名 = ( 元素1,元素2,…,元素n )
元组的绝大多数操作和列表一致,唯一要注意的是元组不可修改
例子:
nums = (1,2,3,4,5)
print(nums)
nums[0] = 0 #出错
注意:
nums = (1,2,3,4,5)
print(nums)
nums = (1,2,3)
print(nums)
这不叫修改元组,这是给nums赋值了一个新的元组,所以没问题。