python for循环

Python for循环可以遍历任何可迭代对象,比如列表,字典,集合,字符串等,通过for循环,可以对可迭代对象里的每一个元组执行一组语句,从原理上看,python的for循环与其他编程语言的for循环不太相似,更像是其他面向对象语言中的迭代器方法

下图是for循环的一般形式
python-for循环一般形式

  1. tmpVar 是临时变量
  2. Iterable 是可迭代对象
  3. 绿色区域是循环条件,当对可迭代对象的迭代遍历结束时,for循环也就结束
  4. block是循环体,每一次循环,程序都要进入到这个block内来执行代码

for循环,从形式上可以分为两种

  1. 使用range函数自由控制循环次数
  2. 遍历容器

1. range函数

range函数返回一个对象,该对象产生一个整数序列。

range函数有3个参数

  1. start 整数序列开始的位置
  2. stop 整数序列结束的位置
  3. step 步长

开始与结束位置所组成的区间[start, stop)是左闭右开的,start默认是0,step默认是1。下面向你展示range函数的使用方法

  1. range(1, 5) 产生的整数序列是1, 2, 3, 4
  2. range(0, 4, 2) 产生的整数序列是0,2
  3. range(5, 0, -1) 产生的整数序列是5, 4, 3, 2, 1
  4. range(3) 产生的整数序列是0,1,2

2. 使用range函数编写for循环

编写for循环输出从1到10(包括10)的整数

for i in range(1, 11):
    print(i)

range(0, 11) 产生了整数序列:1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,结合上一篇教程《迭代遍历思维》的内容来理解这段代码。 range函数产生了一个整数序列,从0到10,for循环的过程就是迭代遍历他们的过程。第一行代码里的i是临时变量,每一次循环,都会改变i的值,i将依次等于1,2 ... 9, 10,而每一次将新的值赋值给i以后,都要进入到循环体里执行代码,在这个示例代码中,循环体里只有一行代码print(i),这样就是实现了输出从1到10的功能。

下图是这段代码的程序流程图
python-for循环流程图
注意看绿色线串起来的部分,恰好形成了一个闭环,这就是循环。

3. 笨拙但朴素,暴力但简单

刚刚学会了for循环,来一道练习题巩固一下吧,请编写程序,输出100以内所有既是2的倍数又是3的倍数的整数。

这个练习题,仍然要使用迭代遍历的思维来解决这个问题,我们不去考虑任何数学上的巧妙算法,仅仅凭借计算机无与伦比的速度,对100以内的每一个整数都进行判断,判断他们是否符合条件

for i in range(101):
    if i % 2 == 0 and i % 3 == 0:
        print(i)

i 依次等于0,1, 2 ... 99, 100, 每循环一次,i的值就改变一次,然后进入循环体对i的值进行条件判断,如果符合条件就使用print函数输出i的值。

是不是很笨拙,你原以为编程是多么高大上的技术,有着常人所不知道秘密,不成想编程竟然就是毫无技术含量的暴力求解,没错,编程就是这样的。但当你习惯这种思维,你会觉得,这是一种很笨拙但是很朴素,很简单的思维,没有任何花里胡哨,没有任何奇技淫巧,就是遍历,从头遍历到尾,每一个数,每一个可能都计算一遍然后得出结果。

那么这么说来,程序员的工作很简单啊!当然不是啦,为了让你更容易接受这个理念和想法,我故意对问题进行了简化,抽象出这么一个你容易理解的描述方式,还存在更复杂的算法,但遍历是所有这些算法的基础,即便是上面这个练习题,仍然可以写出更简单的算法

for i in range(0, 101, 3):
    if i % 2 == 0:
        print(i)

4. 遍历容器数据

在第3章数据类型中,你已经学习了列表,元组,集合,字典4种容器类型数据,他们都是可迭代对象,关于可迭代对象,我会在进阶教程里专门写教程,就目前而言,你只需要知道这个结论即可,下面的代码向你展示如何判断一个数据是不是可迭代对象

from collections import Iterable

print(isinstance(range(5), Iterable))    # range函数返回值是可迭代对象
print(isinstance([1, 2], Iterable))   # 列表
print(isinstance((1, 2, 3), Iterable))   # 元组
print(isinstance({1, 2, 3}, Iterable))   # 集合
print(isinstance({"a": 1, "b": 2}, Iterable))   # 字典

程序输出结果

True
True
True
True
True

4.1 遍历列表,元组

元组和列表实在是太相似了,很多操作方法都是相同的,因此我们总是以列表举例子讲解,遍历一个列表,有3种方法

方法1,通过索引遍历

lst = [2, 4, 6, 7]

for i in range(len(lst)):
    print(lst[i])

方法2,直接遍历

lst = [2, 4, 6, 7]

for item in lst:
    print(item)

方法3, 通过enumerate函数遍历

lst = [2, 4, 6, 7]

for index, item in enumerate(lst):
    print(index, item)

程序输出结果

0 2
1 4
2 6
3 7

这种遍历方式既能获得数据,也能获得数据的索引,是前面两种方法的融合。

4.2 遍历集合

集合没有索引,因此只能直接遍历

set_obj = {1, 2, 3, 3}

for item in set_obj:
    print(item)

4.3 遍历字典

对字典的遍历,有两种常见的方法

方法1, 使用key遍历字典

dic = {
    'a': 1,
    'b': 2
}

for key in dic:
    print(key, dic[key])

方法2, 使用字典items()方法

dic = {
    'a': 1,
    'b': 2
}

for key, value in dic.items():
    print(key, value)

两种方法最终的输出结果是一样的

5. 练习题

练习题是通往熟练的阶梯,熟练是入门的唯一道路

5.1 倒序输出列表里的数据

lst = [1, 5, 6, 2, 4]

请编写程序,倒序输出列表里的数据,输出内容应该是4, 2, 6, 5, 1

5.1.1 思路分析与方法1

range(len(lst)-1, -1, -1)可以产生整数序列4, 3, 2, 1, 0, 恰好是倒序的索引

lst = [1, 5, 6, 2, 4]

for i in range(len(lst)-1, -1, -1):
    print(lst[i])

5.1.2 思路分析与方法2

range(len(lst)) 产生整数序列0,1,2,3,4, 那么可以在使用[]操作符取数据时巧妙的计算出从大到小的索引

lst = [1, 5, 6, 2, 4]

length = len(lst)
for i in range(length):
    print(lst[length-i-1])

5.1.3 思路分析与方法3

继续沿着方法2的思路去想,何不产生出反向索引呢?

lst = [1, 5, 6, 2, 4]

for i in range(len(lst)):
    print(lst[-(i+1)])

一千个人的眼里,有一千个哈姆雷特,一千个程序员完成同样功能的代码,会写出一千种写法,编程不是在无拘无束的状态下让思维天马行空般的翱翔,我们总是面临各种限制,空间与时间总永恒的难以调和的矛盾,在某个限定条件下,程序员要以可接受的时间和空间复杂度要求下完成任务,因此,严格的讲,编程是一种创造性活动。

5.2 找出列表最大值

lst = [4, 6, 1, 7, 2, 9, 3]

编写程序找出列表里的最大值

这个练习题,在教程《迭代遍历思维》里已经有过思路分析和示例代码,但我认为还是有必要再重温一下,温故而知新嘛

lst = [4, 6, 1, 7, 2, 9, 3]

max_value = lst[0]
for item in lst:
    if item > max_value:
        max_value = item

print(max_value)

这段程序,有几处你值得思考的地方

5.2.1 借助额外的变量

在寻找的过程中,要对两个数进行比较,这就必须借助一个额外的变量,来保存这个最大值,借助额外的变量来保存中间结果或者标识某种状态,这种方式无处不在。

5.2.2 为什么让 max_value = lst[0]

不论怎样,都必须给变量max_value一个初始值,唯有如此,才能用它和列表里的数据进行大小比较,其实,这个值你赋值给列表里的哪个数据都可以,如果你已经知道列表里的最小值是1,你甚至可以让max_value等于1,或者比1小的数,都可以

5.2.3 比较的过程为什么不从第2个数开始

前面已经让max_value = lst[0], 而在循环的过程中,max_value与 lst[0]进行了一次比较,这一次比较毫无意义,为何不从列表里第2个数开始比较呢?

你可以从第2个数开始比较,但我不愿意这样做,因为从第2个数开始比较,就必须写成下面的样子

lst = [4, 6, 1, 7, 2, 9, 3]

max_value = lst[0]
for i in range(1, len(lst)):
    if lst[i] > max_value:
        max_value = lst[i]

print(max_value)

很明显,代码写起来变得繁琐了,要花费更多的时间和精力,尽管上一份代码多了一次毫无意义的比较,但是代码书写十分方便。人的时间总是比机器时间更宝贵,计算机多做一次比较所花费的时间连1毫秒都不到,而你为了让计算机少一次比较,或许会多花费1秒钟时间,1秒钟时间很长么?当然很短暂,但是1秒是1毫秒的1000倍。

5.2.4 为什么不用max

肯定会有人会提出这个问题,在这里,我要向你透露一个惊天大秘密,程序员永远不会在工作中自己写算法获取列表的最大值,他们一定使用max函数。那么,我在教程里把它作为一个练习题,还有什么意义么?还有学习的必要吗?

据说去今日头条面试,会要求应聘者手写红黑树,一种非常复杂的数据结构。我相信那些通过面试的人在工作中绝对不会用到红黑树,但只有那些能写出红黑树的人才能胜任头条交给他们的工作。

max函数的确很方便,但你想过没有,python里有多少这种方便的内置函数,屈指可数?不足100个,不论你是从事IT行业的程序员还是其他行业的编程爱好者,我们所面临的问题远比找出列表最大值要复杂,如果你能自己完成找出列表最大值的算法,那么这意味着你的能力可以解决其他同等难度的问题,而这些同等难度的问题极有可能是无法用max等内置函数来解决的。至于难度更大的问题,需要你以此为起点,掌握更多的算法,编写更多的代码,更牢固的记忆语法,更熟练的使用它们,在未来才有机会去解决它们。

虚心使人进步,如果本篇教程你能看到这里,大概率你会将python学习的很好,因为你的求知欲可以驱动你耐心的学习教程,认真且努力。

5.3 寻找列表里第2大的数

一定还有人对5.2中的练习题不直接使用max耿耿于怀,那么我就用这道题目让你明白,仅仅掌握几个内置函数,距离入门还很遥远。

lst = [4, 6, 1, 7, 2, 9, 3]

还是这个列表,请你编写算法,找出列表里第2大的数

python里没有哪个内置函数可以返回列表里第2大的数,这时,估计又有人抬杠了,使用列表的sort方法从小到大排序,lst[-2]就是第2大的数。OK,你赢了,但我提高要求,要求算法时间复杂度是O(n), 你怎么办,任何排序算法的时间复杂度都不是O(n)的,最好也就是 O(n*log2n) 。下面给出代码示例

lst = [4, 6, 1, 7, 2, 9, 3]

max_value = lst[0]
second_max = lst[0]

for item in lst:
    if item > second_max:
        if item >= max_value:
            second_max = max_value
            max_value = item
        else:
            second_max = item

print(second_max)

古人云,求其上,的其中,求其中,得其下,求其下,无所得。我之所以唠叨这么多,是因为总能碰上一些一瓶子不满,半瓶子咣当的人教唆别人放弃学习真正的编程技术,炮制出10分钟入门python,3个月精通python的荒唐言论,以其昏昏使人昭昭。

或许你不是专业搞技术的人,仅仅是爱好,但既然是爱好,难道不应该更加认真的探索学习么?浅尝辄止能算爱好么?

至于是先掌握简单函数用法还是在掌握简单函数用法的同时深入学习,这是方法问题,而不是方向问题,我要指出的是,你不能把仅仅学会几个内置函数的使用作为你学习的方向,正确的方向是自己有能力实现一些简单的算法,前者只需要掌握并记忆一些基础知识,后者除了要掌握基础知识之外还有有能力灵活运用,学习编程的终极目的是具备这种能力,只有这样,你才算掌握一门编程语言,掌握编程技术,才能有机会在工作中使用。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案