Python 学习笔记(05)条件&循环
文章目录
5.1 条件语句
if语句由 关键字本身(if)+ 条件表达式 + 代码块组成,多重条件包括【and/or/not】三种。
if判断为True的情况也有很多种,例如非零数值、非空字符串、非空列表等。单一语句代码块可以放在同一行。
if cond_1_expr:
...
elif cond_2_expr:
...
elif cond_3_expr:
...
else:
print("This should be the last condition")
# 条件表达式(三元操作符)
smaller = x if x < y else y
5.2 循环语句
5.2.1 while 条件循环
while condition() != True:
...
<else: ...>
"""
for...else...和 while...else...中else子句只在【循环正常完成后】执行,如果循环由于循环体中break语句退出则不会执行else子句,因此可以使用其测试循环是否正常结束。
"""
5.2.2 for 迭代循环
for循环原理为遍历所有成员,依次访问可迭代对象(序列、迭代器等)。
1)for循环遍历字符串、列表、元组等序列类型时,可以通过序列项、序列索引或通过**enumerate()**函数建立【索引&项】元组的形式进行迭代,其中直接迭代序列比通过索引的方式更快;
2)for循环遍历迭代器时,自动调用迭代器对象 **next()**方法,调用后返回【下一个条目】,直到捕获 StopIteration异常时结束循环。
list_1 = ['list', 'tuple', 'dict', 'set']
# 1)直接迭代序列
for item in list_1:
print(item)
# 2)通过序列索引迭代
for i in range(len(list_1)):
print(list_1[i])
# 3)通过enumerate()函数迭代
for i, value in enumerate(list_1):
print(i, value)
5.2.3 循环控制
1)break
停止执行整个循环,直接跳出。
2)continue
跳过本次执行,进入下一次循环。在条件循环中,需要验证条件表达式;在迭代循环中,需要验证是否还有可迭代的元素。满足条件后才会进行continue。
3)pass
代表空语句,主要作用为在开发、调试过程中用来先确定结构,或是在异常处理中暂时不采取任何措施。
pass不仅可以在循环中使用,可以出现在任何需要【语句块】的地方。
5.3 相关内建函数(BIF)
range(start, end, step) range(end) range(start, end)
"""
用于创建一个循环范围,可以结合len()实现【下标】控制循环。
"""
enumerate() # 每次循环返回tuple(索引-元素对)
reversed() # 逆序访问
zipped = zip(seq_1, seq_2, ...)
"""
用于聚合列表,从多个等长序列中依次取出一个元素组成【元组】;
相对应的接触聚合 seq_1, seq_2 = zip(*zipped)
"""
sorted(iterable, key=None, reverse=False)
"""
iterable 表示指定的序列,key 参数可以自定义排序规则;reverse 参数指定以升序(False,默认)还是降序(True)进行排序。sorted() 函数会返回一个排好序的列表。
"""
5.4 迭代器
迭代是python中访问集合元素的一种循环方式,迭代器是一个可以记住遍历位置的对象,其主要目的是为【类序列对象】提供类序列接口,例如字典的键、文件的行等,用于迭代不是序列但表现出序列行为的对象。
如何判断是否对象是否可迭代?
from collections import Iterable
isinstance(class, Iterable)
"""
可迭代对象都含有__iter__方法,只要通过isinstance()和Iterable检查自定义类创建的实例对象即可。
"""
5.4.1 迭代对象 可迭代对象
迭代对象是指实现了__iter__与 next() 方法的对象;
可迭代对象可以只实现__iter__,也可以两个都实现,有些可迭代对象的迭代对象就是它本身。如果一个可迭代对象同时是它的迭代对象,则无法重复迭代。
# 使用iter()可以获取一个可迭代对象的迭代对象
iter(obj) # obj = str/list/tuple
# 判断可迭代对象的迭代对象是不是它本身
print(iter(obj) is obj)
迭代器的本质并非【索引计数】,而是使用 next() 函数。迭代器自动调用迭代器对象 next()方法,调用后返回下一个条目,直到捕获StopIteration异常后结束循环。
5.4.2 迭代器的使用
import sys
# for循环
fetch = iter(seq)
for i in fetch: # 通过循环访问迭代器
do_something_to(i)
while True: # 通过next()访问迭代器
try:
next(fetch)
except StopIteration:
sys.exit()
# 字典
dict_1.keys() dict_1.values() dict_1.items()
# 文件
readline() # for eachline in open('*.txt')
5.4.3 迭代器注意事项
1)对于【可变对象】,迭代时不允许添加、删除元素
对于列表,迭代器记录的是当前到达第几个元素,改变列表更新会立刻反应到【迭代条目】上,会引发迭代混乱问题;
对于字典,修改会引发错误,切记不可改变字典。
2)迭代器限制
迭代器的移动方向只能向后,不可回到开始;
迭代器无法复制,只能创建另一个迭代器。
5.5 列表解析
列表解析放在方括号中,因为最后构建出来的是一个新的列表(python 3.X可以用解析构造元组、字典)。
# 常规方式修改列表
for i in range(len(list_1)):
list_1[i] += 10
# 列表解析基本语法
list_1 = [x + 10 for x in list_1] # [expr for iter_var in iterable]
# 列表解析拓展语法
[expr for iter_var in iterable if cond_expr]
# 双层循环形成全排列
file_1 = open('*.txt')
print([word for line in file_1 for word in line.spilt()]) # [expr(a, b) for a in iter_1 for b in iter_2]
# 同时使用dict的键、值
dict_1 = {'key': 'value'}
print([key for key, value in dict_1.items()])
# 与zip结合使用
print([x**2 for (x, y) in zip(seq_1, seq_2) if y > 10])
5.6 生成器
生成器可以理解为生成数据的工具,按照某种规则不断的生成数据直至结束。
从形式上生成器仅仅是将列表解析式的方括号换成了圆括号,实际使用上生成器和列表解析式有很大的差别。列表解析需要一次生成所有数据,若列表元素过多则会占据很大的内存空间;而生成器可以看作列表解析和迭代器的结合,在循环中一边循环一边计算后续元素,一次仅生成一个元素,即优化了内存使用又可以获取完整的列表内容。
5.6.1 生成器表达式
list_1 = [x**2 for x in range(1, 20) if x % 2 == 0]
# 创建生成器
generator_1 = (x**2 for x in range(1, 20) if x % 2 == 0)
print(type(list_1))
print("list_1: ", list_1)
print(type(generator_1))
print("generator_1: ", generator_1)
Out:
<class 'list'>
list_1: [4, 16, 36, 64, 100, 144, 196, 256, 324]
<class 'generator'>
generator_1: <generator object <genexpr> at 0x000001F3817036D0>
"""
可以看到,使用列表解析式即生成了一个完整的列表,包含了列表所有元素;而使用生成器产生的不包含任何元素。
"""
# 获取生成器中的值
print(next(generator_1), '\n') # generator_1.__next__()
# 生成器循环元素
for i in generator_1:
print(i)
Out:
4
16
36
64
100
144
196
256
324
"""
当对生成器进行迭代时,可以获取生成器生成的整个列表的元素,但生成器只能迭代一次,完成后将无法获取内容。
由于代码中先使用next方法取出了一个生成器中的数,后面 for循环中迭代并不包含这个值。
生成器的内涵在于 允许返回一个值同时“暂停”代码的运行,需要时再“恢复”运行。
"""
5.6.2 yield关键字实现生成器
# 创建生成器
def generate(start, end):
for n in range(start, end):
if n % 2 == 0:
yield n**2
"""
原本def用法是定义函数,但当函数体中 return 被替换为 yield 后,便可以用作定义生成器;
yield可以看作和 return功能近似即返回;函数体内可以定义多个yield;
yield实现的生成器可以使用for循环和next()进行迭代。
"""
items = generate(1, 20)
for i in items:
print(i)
Out:
4
16
36
64
100
144
196
256
324
# yield 详细解释
def func():
print("starting...")
while True:
res = yield 1
print("res: ", res)
generator_1 = func()
print(next(generator_1))
print("~"*20)
print(next(generator_1))
print("~"*20)
print(generator_1.send(-1))
Out:
starting...
1
~~~~~~~~~~~~~~~~~~~~
res: None
1
~~~~~~~~~~~~~~~~~~~~
res: -1
1
"""
1.程序开始运行后,因为fun()中含有关键字yield,所以调用时得到一个生成器对象generator_1;
2.当调用next(generator_1)方法时,fun()函数开始执行,先执行函数体中print方法,再进入while循环;
3.程序遇到yield关键字,将其视为return,此时执行第一个print(next(generator_1))输出1,之后函数暂停;
4.注意:函数暂停时并没有执行赋值给res的操作,第一条next(generator_1)结束;
5.print("~"*20);
6.开始执行第二条next(generator_1),继续刚才还未赋值的状态。但此时由于yield将值返回到了主程序中,导致赋值右边没有值可赋,此时为None。所以接下来输出的就是res: None;
7.程序继续运行,第二次碰到yield,此时执行第二个print(next(generator_1))输出1,之后函数暂停;
8.print("~"*20);
9.send()函数作用在于传递一个值给res,注意第二次暂停还是停在赋值操作。由于send()将值赋给了res,因此输出res: -1;
10.由于send()函数中包含next()方法,所以程序会继续向下运行,直到第三次碰到yield,print(generator_1.send(-1))输出1后停止。
"""