[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】

本文仅供学习使用

Python基础——Ch07~Ch13

  • 7. 运算符、优先级
    • 7.1 算数运算符
    • 7.2 比较运算符
    • 7.3 赋值运算符
      • 7.3.1 增强赋值
      • 7.3.2 序列赋值
      • 7.3.3 多目标赋值
    • 7.4 逻辑运算符
    • 7.5 成员运算符
    • 7.6 身份运算符
    • 7.7 位运算符
    • 7.8 运算符优先级
    • 7.9 +、+=、* 的连接操作
  • 8. 条件语句
    • 8.1 三元表达式
    • 8.2 if 语句嵌套
    • 8.3 条件语句特点
    • 8.4 实现:石头剪刀布游戏
  • 9. 循环语句
    • 9.1 while 循环
    • 9.2 for 循环
    • 9.3 循环控制语句
    • 9.4 优化:石头剪刀布游戏
    • 9.5 pass 语句
  • 10. 推导式
    • 10.1 列表推导式
    • 10.2 字典推导式
    • 10.3 集合推导式
  • 11. 列表、字典、集合迭代问题
    • 11.1 列表内存自动管理
    • 11.2 字典、集合遍历问题
  • 12. 函数
    • 12.1 自定义函数
    • 12.2 return用法
    • 12.3 文档注释
    • 12.4 类型标注
    • 12.5 参数传递
    • 12.6 参数分类
    • 12.7 匿名函数
  • 13. 封包、解包、迭代器、生成器
    • 13.1 封包
    • 13.2 解包
    • 13.3 迭代器
    • 13.4 生成器


7. 运算符、优先级

7.1 算数运算符

[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第1张图片

a = 3 
b = 2 
d = a + b 
print(d) # 5 
d = a - b 
print(d) # 1 
d = a * b 
print(d) # 6 
d = a / b 
print(d) # 1.5 
# 取模(除法的余数)
d = a % b 
print(d) # 1 

# 相当于 / 再向下取整 
d = a // b 
print(d) # 1 

# 10**8 还可以用科学计数法写成 1e8 
d = a ** b 
print(d) # 9 
d = 10 ** 8 
print(d) # int 
f = 1e8 
print(f) # float

负数取整,取模

print(15 // 4) # 15 / 4等于3点几,然后再向下取整,取更小的最接近的整数3
 print(-15 // 4) # -15 / 4等于负3点几,然后再向下取整,取更小的最接近的整数-4 
 print(15 % 4) # 余数 = 15 - (3*4) = 3 
 print(-15 % 4) # 余数 = -15 - (-4*4) = 1 
 # 规律: (a % b) + (-a % b) = b或0 (当a为0时,为0)

7.2 比较运算符

返回布尔值:True,False
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第2张图片

a = 20 
b = 9 
c = a == b 
print(c) # False
 c = a != b 
 print(c) # True 
 c = a > b 
 print(c) # True 
 c = a < b 
 print(c) # False 
 c = a >= b 
 print(c) # True 
 c = a <= b 
 print(c) # False

7.3 赋值运算符

[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第3张图片

a = 3 
c = a + 2 
print(c) # 5 
c += a 
print(c) # 8 结果等于 c = c + a 
c -= a 
print(c) # 5 结果等于 c = c - a 
c *= a 
print(c) # 15 结果等于 c = c * a 
c /= a 
print(c) # 5.0 结果等于 c = c / a 
c %= a 
print(c) # 2.0 结果等于 c = c % a 
c **= a 
print(c) # 8.0 结果等于 c = c ** a 
c //= a
print(c) # 2.0 结果等于 c = c // a

:= 海象运算符

# 写法一: 
string = "hello world" 
length = len(string) 
print(length + 5) 
print(f"string的长度为{length}") 

# 写法二: 
string = "hello world" 
print(len(string) + 5) 
print(f"string的长度为{len(string)}") 

# 写法三: 
# 海象运算符,可在表达式内部为变量赋值,记得加括号,不然就是赋值len(string) + 5的返回值了 
# 相对写法一:避免了一次赋值给中间变量的步骤 
# 相对写法二:避免使用两次len方法 
string = "hello world" 
print((length := len(string)) + 5) 
print(f"string的长度为{length}")

7.3.1 增强赋值

增强赋值在条件符合的情况下(例如:操作数是一个可变类型对
象)会以追加的方式来进行处理,而普通赋值则会以新建的方式进
行处理,此时增强赋值语句比普通赋值语句的效率是更高的,但是
也要注意因此产生的问题

""" 增强的赋值运算符 += -= *= /= %= **= //= """ 
lis1 = [1, 2] 
lis2 = lis1
lis1 = lis1 + [3, 4] 
print(lis1) # [1, 2, 3, 4] 
print(lis2) # [1, 2] 

lis1 = [1, 2] 
lis2 = lis1 
lis1 += [3, 4] 
print(lis1) # [1, 2, 3, 4] 
print(lis2) # [1, 2, 3, 4] 

# a = 12 
# b = a 
# a = a + 1 
# print(a) # 13 
# print(b) # 12 

# a += b # 等效于 a = a + b 

# a = 12 
# b = a 
# a += 1 # 等效于 a = a + 1 
# print(a) # 13 
# print(b) # 12

7.3.2 序列赋值

a, b = 3, 4 # 等同于 (a,b) = (3,4) 
print(a, b) 
[a, b] = [3, 4] 
print(a, b) 
[a, b] = [3, 4] 
print(a, b)

a, b, c = [3, 4, 5] 
print(a, b, c) 
a, b, c = "你好吗" 
print(a, b, c)

7.3.3 多目标赋值

a = b = c = 999 
d = 999 
print(id(a)) 
print(id(b)) 
print(id(c)) 
print(id(d)) 
b = d 
print(id(b)) 
a = b = c = [1, 2, 3] 
b.append(4) 
print(a, b, c)

7.4 逻辑运算符

[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第4张图片

# None, False, 0, 空列表/空元组/空字典/空集合都会被认定为 False 
a = 2 
b = "hello world" 
c = [] 
d = 0 

# 左边为假,返回左边;否则返回右边 
print(c and a) # [] 
print(c and d) # [] 
print(d and c) # 0 
print(a and c) # [] 
print(a and b) # "hello world" 
print(b and a) # 2 

# 左边为真,返回左边;否则返回右边 
print(a or c) # 2 
print(a or b) # 2 
print(b or a) # "hello world" 
print(c or a) # 2 
print(c or d) # 0 
print(d or c) # [] 

# 假,返回真;真,返回假 
print(not a) # False 
print(not b) # False 
print(not c) # True 
print(not (a and c)) # True 

# 优先级:not > and > or 
print(not a or c) # []

规律:
and 只要有其中一边为 False,最后结果一定为 False;只有两边都为 True 的情况下,最后结果才为 True。
or 只要有其中一边为 True,最后结果就为 True;只有两边都为 False 的情况下,最后结果才为 False。

短路原则: 如果前面的部分已经计算出整个表达式的结果,则后面的部分不再计算(考虑优先级)。

a = 0 
b = 1 
print(a == 0 or b / a > a) 
print(a > 0 and b / a > a) 
print(a > 0 or b / a > a)

7.5 成员运算符

返回布尔值:True,False(判断对象中是否存在某个元素)
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第5张图片

str1 = "hello world" 
list1 = [1, 2, 3, 4, 5] 
tuple1 = (1, 2, 3, 4, 5) 
set1 = {1, 2, 3, 4, 5} 
a = "hel" in str1 
b = 4 in list1 
c = 4 in tuple1 
d = 4 in set1 
print(a,b,c,d) # True True True True

a = "hel" not in str1 
b = 4 not in list1 
c = 4 not in tuple1 
d = 4 not in set1 
print(a,b,c,d) # False False False False

7.6 身份运算符

返回布尔值:True,False
在这里插入图片描述

a = 257 
b = 257 
print(a == b) 
print(a is b) 
print(id(a)) 
print(id(b)) 
a = 256 
b = 256 
print(a == b) 
print(a is b) 
print(id(a)) 
print(id(b))

is 和 == 的区别
is 是身份运算符,判断对象的地址是否相同;而 == 是比较运算符,判断对象的值是否相等
判断两个变量是否相等,实际上式需要判断两个条件:

  1. 变量的值是否相等;
  2. 变量的内存地址是否相同。

7.7 位运算符

位运算符是把数字看作二进制来进行计算的
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第6张图片
编码

ASCII编码:

字符串是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte)。

所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。

由于计算机是美国人发明的,因此,最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。

Unicode:

Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。

现代操作系统和大多数编程语言都直接支持Unicode。

现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节

新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。
所以,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

进制转换函数

bin(x)
将一个整数转变为一个前缀为“0b”的二进制字符串。

print(bin(3)) # 0b11 
print(bin(-100)) # -0b1100100

hex(x)
将一个整数转变为一个前缀为“0x”的十六进制字符串。

print(hex(255)) # 0xff 
print(hex(-42)) # -0x2a

oct(x)
将一个整数转变为一个前缀为“0o”的八进制字符串。

print(oct(255)) # 0o377 
print(oct(-42)) # -0o52
a = 60 # 二进制 60 = 0011 1100 
b = 13 # 二进制 13 = 0000 1101 
c = a & b # 12 = 0000 1100 同都为真(1和1), 才为真 
print(c) 

c = a | b # 61 = 0011 1101 一个真(至少一个1), 就为真 
print(c) 

c = a ^ b # 49 = 0011 0001 不同(01或者10), 就为真 
print(c) 

c = a ^ b ^ a # 一个数异或于同一个数两次,结果还是它本身 
print(c) # 13

在计算机中,负数以原码的补码形式表达
负数的反码:对该数的原码除符号位外各位取反
负数的补码:对该数的原码除符号位外各位取反,然后在最后一位加1 (即反码+1)
正数的反码和补码都与原码相同

# 23的原码:0,0010111 右移两位舍弃右边的两位,最高位使用符号位填充:000,00101 还原成十进制就是5
# 右移三位舍弃右边的三位:0000,0010 还原成十进制就是2 
print(23 >> 2, 23 >> 3) 

# 23的原码:0,0010111 左移两位后面补两个0:0,001011100 换算成十进制就是92
# 左移三位后面补三个0:0,0010111000 换算成十进制就是184 
print(23 << 2, 23 << 3)

# 因为在计算机中,负数以原码的补码形式表达,所以先算出补码,再来移位(符号位1代表负数) 
# -23原码:1,0010111 -23反码:1,1101000 -23补码: 1,1101001 
# 补码向右移两位得到:111,11010 这个还是补码形式,需要先转成原码再换算成十进制 
# 补码转成原码就是逆过程:111,11010减一再取反码就是原码了,得到:111,00110 换算成十进制就是-6
# 补码向右移三位得到:1111,1101 这个还是补码形式,需要先转成原码再换算成十进制 
# 补码转成原码就是逆过程:1111,1101减一再取反码就是原码了,得到:1111,0011 换算成十进制就是-3 
# 补码向左移两位得到:1,110100100 这个还是补码形式,需要先转成原码再换算成十进制 
# 补码转成原码就是逆过程:1,110100100减一再取反码就是原码了,得到:1,001011100 换算成十进制就是-92
# 补码向左移三位得到:1,1101001000 这个还是补码形式,需要先转 成原码再换算成十进制 
# 补码转成原码就是逆过程:1,1101001000减一再取反码就是原码了,得到:1,0010111000 换算成十进制就是-184 
print(-23 >> 2, -23 >> 3) 
print(-23 << 2, -23 << 3)

按位取反运算符,对数据的每个二进制位取反(包括符号位)

# 23的原码:0,0010111 按位取反:1,1101000 
# 按位取反之后变成了负数, 负数在计算机中以原码的补码形式表达,所以需要先转成原码再换算成十进制 
# 补码转成原码就是逆过程:1,1101000减一再取反码就是原码了,得到:1,0011000 换算成十进制就是-24 
print(~23) 

# 负数在计算机中以原码的补码形式表达,所以先算出补码,再来按位取反 
# -23原码:1,0010111 -23反码:1,1101000 -23补码: 1,1101001 
# 再来按位取反得到:0,0010110 按位取反之后变成了正数,正数直接换算成十进制就是22 
print(~-23)

7.8 运算符优先级

以下表格列出了从高到低优先级的常用运算符:
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第7张图片
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第8张图片

7.9 +、+=、* 的连接操作

+、+=、* 还可以对字符串、列表、元组进行连接操作(要保证连接对象的类型一致)

a = "1" 
b = "2" 
c = a + b 
print(c) # '12' 
a = ["1"] 
b = [2, 3] 
c = a + b 
print(c) # ['1', 2, 3] 
a = ("1",) 
b = (2, 3) 
c = a + b 
print(c) # ('1', 2, 3) 
a = "1" 
b = "2" 
a += b # a = a + b 
print(a) # '12' 
a = ["1"] 
b = [2, 3] 
a += b 
print(a) # ['1', 2, 3]
a = ("1",) 
b = (2, 3) 
a += b 
print(a) # ('1', 2, 3) 
a = "1" 
b = a * 3 
print(b) # "111" 
a = ["1"] 
b = a * 3 
print(b) # ['1', '1', '1'] 
a = ("1",) 
b = a * 3 
print(b) # ('1', '1', '1')

8. 条件语句

格式一:
if 判断条件:
执行语句…

sex = '男' 
if sex == '男': 
	print("男士请进") 
	print("这是男士专用卫生间...") 
sex = "女" 
if sex == '女': 
	print("女士请进"); 
	print("这是女士专用卫生间...")

格式二 :
if 判断条件:
执行语句…
else:
执行语句…

sex = '男' 
if sex == '男': 
	print('男士请进') 
else:
	print('女士请进')

格式三:
if 判断条件1:
执行语句1…
elif 判断条件2:
执行语句2…
elif 判断条件3:
执行语句3…
else:
执行语句4…

sex = input("请输入你的性别 (男/女): ") 
if sex == "男": 
	print("男士请进") 
elif sex == "女": 
	print("女士请进") 
else:
	print("输入错误!") 
	print("程序结束")

8.1 三元表达式

它是一个表达式,而不是一个结构化的代码块

score = float(input("请在下方输入你的考试分数:\n")) 
if score >= 90: 
	print("牛逼") 
elif score >= 80: 
	print("优秀") 
elif score >= 60: 
	print("可以") 
# elif score < 60: 
# 	print("同志仍需努力") 
else:
	print("同志仍需努力") 

# 三元表达式 
result = print("牛逼") if score >= 90 else print("优秀") if score >= 80 else print("可以") if score >= 60 else print("同志仍需努力") 
print(result) # None 

if score >= 90: 
	result = "牛逼" 
elif score >= 80: 
	result = "优秀" 
elif score >= 60:
	result = "可以"
else:
	result = "同志仍需努力" 
print(result) 

# 三元表达式 result = "牛逼" if score >= 90 else "优秀" if score >= 80 else "可以" if score >= 60 else "同志仍需努力" 
print(result) 

print("老师说我" + ("牛逼" if score >= 90 else "优秀" if score >= 80 else "可以" if score >= 60 else "同志仍需努力"))

8.2 if 语句嵌套

print("敲门声: 咣咣咣~")
sex = input("请输入你的性别 (男/女): ") 
if sex == "男": 
	print("滚犊子...") 
else:
	answer = input("请问是不是包租婆? (是/不是): ") 
	if answer == '是': 
		print("屋里没人...") 
	else:
		answer2 = input("请问是不是小姐姐? (是/不是): ") 
		if answer2 == '是': 
			print("请进, 你是找我的吗?") 
		else:
			print("不好意思, 您找错人了...") 

# 三元表达式嵌套(了解一下)
print("不好意思, 我今天不能见男的, 您请回吧") if input("请问你是男是女 (男/女): ") == "男" else (print("不好意思, 今天我不在家, 房租下次给你") if input("请问是包租婆吗? (是/不是): ") == "是" else (print("小姐姐, 您好, 请进...") if input("请问你是小姐姐吗? (是/不是): ") == "是" else print("不好意思, 我今天不宜面圣, 您请回吧")))

8.3 条件语句特点

每个条件语句只会满足一次结果

num = 5 
if num > 0: # 满足条件, 输出a, 后面就不会执行了 
	print("a") 
elif num > 1: 
	print("b") 
elif num > 2: 
	print("c") 
elif num > 3: 
	print("d") 
else:
	print("e") 
if num > 4: # 然后又跳到这个条件语句中执行 
	print("f") 
else:
	print("g")

进行 bool 判断时,任何非零 / 非None / 非空值为 True,反之为False

if 123456: 
	print("123456为True") 
if []:
	print("[]为True") 
else:
	print("[]为False")

8.4 实现:石头剪刀布游戏

import random 
INFO = {0: "石头", 1: "剪刀", 2: "布"} # ["石头", "剪 刀", "布"] 
computer = random.randint(0, 2) 
player = int(input("请出拳! 输入0代表石头、1代表剪刀、2代表 布: ")) 

# 展示电脑和玩家的出拳结果 
print(f"电脑出拳: {INFO[computer]}\n玩家出拳: {INFO[player]}") 

# 判断谁输谁赢 
if computer == player: 
    print("平局!") 
elif player-computer == -1 or player-computer == 2: 
    print("玩家胜利!") 
else:
    print("电脑胜利!")

random 模块常用函数
random.random() 返回 [0.0, 1.0) 范围内的随机浮点数
random.randint(a, b) 返回 [a, b] 范围内的随机整数
random.uniform(a, b) 返回 [a, b] / [b, a] 范围内的随机浮点数
random.choice(seq) 从非空序列 seq 返回一个随机元素。 如果seq 为空,则引发 IndexError
random.sample(population, k) 从序列或者集合中随机获取k个元素,以列表形式返回(Python3.9 版本, 集合中采样已弃用)
random.shuffle(x) 将可变序列 x 随机打乱位置
random.randrange ([start,] stop [,step]) 等效于从 range(start, stop, step) 里随机返回一个元素
random.seed ([x]) 起固定随机数的作用,x 可以是任意数字(x可以理解为种子的名字)

import random 
# 返回 [0.0, 1.0) 范围内的随机浮点数 
a = random.random() 
print(a) 

# 返回 [2, 4] 范围内的随机整数 
a = random.randint(2, 4) 
print(a) 

# 返回 [2.5, 3.5] 范围内的随机浮点数 
a = random.uniform(2.5, 3.5)
a = random.uniform(3.5, 2.5) 
print(a) 

# 从序列返回一个随机元素 
a = random.choice(range(10)) 
print(a) 

# 从序列中随机获取k个元素,以列表形式返回 
a = random.sample(("a", 1, 2, 3, "b"), 2)
print(a) 

# 从集合中随机获取k个元素,以列表形式返回 
a = random.sample({"a", 1, 2, 3, "b"}, 2) 
print(a) 

# 将可变序列随机打乱位置 
a = [1, 2, 3, 4, 5] 
random.shuffle(a) 
print(a) 

# 从[star, stop)范围按照step步长组成的数据范围里返回一个随机数 
a = random.randrange(start=2, stop=100, step=2) 
print(a) 

# 设定好种子之后,每次执行程序随机数将是固定的 
# 使用相同的随机数种子,生成的随机数值是一样的 
print(random.random()) 
random.seed(10) 
print("seed10:", random.random()) 
random.seed(7) 
print("seed7:", random.random()) 
random.seed(10) 
print("seed10:", random.random())

9. 循环语句

9.1 while 循环

格式:
while 判断条件:
执行语句…

a = 1 
while a < 4: 
	print(a) # 1 2 3 
	a += 1

while True:

a = 0 
while True: 
	print("hello") 
	a += 1 
	if a == 3: 
		break

while … else

count = 0 
while count < 5: 
	print(count, "小于5") 
	count = count + 1 
else:
	print(count, "大于或等于 5")
	
count = 0 
while count < 5: 
	print(count, "小于5") 
	count = count + 1 
print(count, "大于或等于 5") 
	
count = 0 
while True: 
	print(count, "小于5") 
	count = count + 1 
	if count >= 5: 
		break 
	else:
		print(count, "大于或等于 5") 

count = 0 
while True: 
	print(count, "小于5") 
	count = count + 1 
	if count >= 5: 
		break 
print(count, "大于或等于 5")

while 循环嵌套

# 实现九九乘法表 
right = 1 
while right <= 9: 
	left = 1 
	while left <= right: 
		print(f"{left}x{right}={left*right}", end="\t")
		left += 1 
	print() 
	right += 1

9.2 for 循环

for 变量 in 可迭代对象:
执行语句…

str1 = "123ab" 
lis1 = [1, 2, 3, "a", "b"] 
tup1 = (1, 2, 3, "a", "b") 
dic1 = {"one": 1, "two": 2} 
set1 = {3, 2, 4, "a"} 
for i in str1: 
	print(i) 
for i in lis1: 
	print(i) 
for i in tup1: 
	print(i) 
for i in dic1: 
	print(i)
for i in set1: 
	print(i)

当循环变量不用时可以使用“_”代替

for … in … else

for i in [1, 2, 3, 4]: 
	print(i) 
	if i > 2: 
		break 
else:
	print(5) 

for i in [1, 2, 3, 4]: 
	print(i) 
	if i > 2: 
		break 
print(5)

for 循环嵌套

# 实现九九乘法表 
for right in range(1, 10): 
	for left in range(1, right+1): 
		print(f"{left}x{right}={left*right}", end="\t") 
	print()

range(stop) / range(start, stop[, step])
返回一个按步骤生成的从start(包括)到stop(不包括)的整数序列,它是不可变的序列
start:计数从 start 开始,默认是从 0 开始
stop:计数到 stop 结束,但不包括 stop
step:步长,默认为1
range 类型相比常规 list 或 tuple 的优势在于一个 range 对象总是占用固定数量的(较小)内存,不论其所表示的范围有多大(因为它只保存了 start, stop 和 step 值)

print(list(range(4))) # [0, 1, 2, 3] 
print(list(range(1, 5))) # [1, 2, 3, 4] 
print(list(range(1, 8, 2))) # [1, 3, 5, 7] 
print(list(range(8, 1, -2))) # [8, 6, 4, 2]

enumerate(iterable, start=0)
返回一个enumerate对象(迭代器)。迭代它会得到一个个的元组,每个元组是索引(从start开始,默认为 0)和索引对应iterable的值组成的

seasons = ['Spring', 'Summer', 'Fall', 'Winter'] 
object1 = enumerate(seasons) #  
print(list(object1)) # [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')] 
object2 = enumerate(seasons, start=1) # 设置开始迭代的索引
print(list(object2)) # [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

9.3 循环控制语句

break
终止所在的循环

for i in range(3): 
	for j in range(3): 
		print("hello") 
		break # 只终止所在的循环, 所以输出3个 hello

continue
跳过当前,继续到循环位置

a = 0 
while a <= 5: 
	if a == 3: 
		a += 1 
		continue 
	print(a) 
	a += 1

9.4 优化:石头剪刀布游戏

import random
INFO = {0: "石头", 1: "剪刀", 2: "布"} # ["石头", "剪 刀", "布"] 
while True: 
	computer = random.randint(0, 2) 
	while True: 
		player = int(input("请出拳! 输入0代表石头、1代表剪 刀、2代表布: "))
		# 判断输入是否有误 
		if player in [0, 1, 2]: 
			# 展示电脑和玩家的出拳结果 
			print(f"电脑出拳: {INFO[computer]}\n玩家出拳: {INFO[player]}") 
			# 判断谁输谁赢 
			if computer == player: 
				print("平局!") 
			elif player-computer == -1 or player- computer == 2: 
				print("玩家胜利!") 
			else:
				print("电脑胜利!") 
			break 
		print("输入有误, 输入0代表石头、1代表剪刀、2代表布")

 # 判断输入是否有误 
 	while True: 
 		rec = input("是否继续游戏?(y/n): ") 
 		if rec in ["y", "n", "Y", "N"]: 
 			break 
 		else:
 			print("输入有误, 请输入y或者n")
# 判断游戏是否要继续玩 
	if rec in ("n", "N"): 
		print("游戏结束!") 
		break

9.5 pass 语句

pass 是一个空操作, 当它被执行时, 什么都不发生。它适合当语法上需要一
条语句但并不需要执行任何代码时用来临时占位

num = int(input("请输入一个数字: ")) 
if num < 100: 
	pass 
else:
	pass

10. 推导式

10.1 列表推导式

列表推导式(list comprehension)是利用其它列表创建新列表的一种方式,工作原理类似for循环,即可对得到的元素进行转换变形 ,其基本格式如下(可以有多个for语句)

[expr for value in collection ifcondition]
expr可以使for循环的变量,也可以是表达式

结构由一对方括号里面包含一个表达式,后面跟一个 for 子句,然后是零个或多个 for 或 if 子句组成
其结果将是一个新列表,由表达式依据后面的 for 和 if 子句的内容进行求值计算而得出

squares = [] 
for x in range(10): 
	squares.append(x**2) 
squares = [x**2 for x in range(10)]

result = [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] 
result = [] 
for x in [1,2,3]: 
	for y in [3,1,4]: 
		if x != y: 
			result.append((x, y))

嵌套的列表推导式
列表推导式中的初始表达式可以是任何表达式,包括另一个列表推导式

matrix = [[1, 2, 3, 4], 
		[5, 6, 7, 8], 
		[9, 10, 11, 12]] 
result = [[row[i] for row in matrix] for i in range(4)] 
result = [] 
for i in range(4): 
	result.append([row[i] for row in matrix])
result = [] 
for i in range(4): 
	temp = [] 
	for row in matrix: 
		temp.append(row[i]) 
	result.append(temp) 
result = list(zip(*matrix))

10.2 字典推导式

dic = {x: x**2 for x in range(4)} 
print(dic) 

dic = {x: x**2 for x in range(6) if x % 2 == 0} 
print(dic) 

dic = {k: v for k, v in zip((1, 2, 3), (4, 5, 6))} 
print(dic)

10.3 集合推导式

集合推导式跟列表推导式非常相似,唯一区别在于用{}代替[]。其基本格式如下:

{ expr for value in collection if condition }

set1 = {x**2 for x in range(4)} 
print(set1) 

set1 = {x for x in 'abracadabra' if x not in 'abc'} 
print(set1)

11. 列表、字典、集合迭代问题

11.1 列表内存自动管理

lis = [1, 2, 3] 
for item in lis: 
	lis.remove(item) 
print(lis)

如果原列表在内存中为:
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第9张图片
那么第一次遍历执行 lis.remove(item) 时,将索引0对应的元素删除(即删除元素1),然后对列表内存进行收缩,使得后面的元素2、3都往前移动,则列表在内存中变为:
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第10张图片
第二次遍历执行 lis.remove(item) 时,将索引1对应的元素删除(即删除元素3);第三次遍历时会因为超出索引范围而停止遍历,所以最终的列表为[2]
[深度学习]Part1 Python学习基础Ch07~Ch13——【DeepBlue学习笔记】_第11张图片
列表内存自动管理功能: 在删除列表中的元素时,Python会自动对列表内存进行收缩,并移动列表中的元素以保证元素之间没有间隙,所以遍历删除列表中的元素时,被删元素后面的值会向前顶,导致漏删。

如何解决这个问题呢?
思路:遍历一个新的列表,不受原数据修改的影响即可

import copy
lis = [1, 2, 3] 
lis2 = [1, 2, 3] # 思考:换成 lis2 = lis 是否可以? 
for item in lis2: 
	lis.remove(item) 
print(lis) 

lis = [1, 2, 3] 
for item in lis[:]: # 浅拷贝 
	lis.remove(item) 
print(lis) 

lis = [1, 2, 3] 
lis2 = lis.copy() # 浅拷贝 
for item in lis2: 
	lis.remove(item) 
print(lis) 

lis = [1, 2, 3] 
lis2 = copy.copy(lis) # 浅拷贝 
for item in lis2: 
	lis.remove(item) 
print(lis) 

lis = [1, 2, 3] 
lis2 = copy.deepcopy(lis) # 深拷贝 
for item in lis2: 
	lis.remove(item) 
print(lis) 

lis = [1, 2, 3] 
lis2 = list(lis) # 返回一个新的对象,同理:tuple()/set() 也可以 
for item in lis2:
	lis.remove(item) 
print(lis)

11.2 字典、集合遍历问题

字典、集合在遍历时,如果改变原数据的大小,则会造成迭代时报错

dic = {"name": "Tom", "age": 18, "height": 188} 
for key in dic: 
	dic.pop(key) # 改变了原数据大小,所以报错 
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
for key in dic: 
	dic.update({"weight": 88}) # 改变了原数据大小,所以报错
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
for key in dic: 
	dic.update({"age": 22}) # 没有改变原数据大小,所以不报错
print(dic)
set1 = {"name", "age", "height"} 
for key in set1: 
	set1.pop() # 改变了原数据大小,所以报错 
print(set1)

set1 = {"name", "age", "height"} 
for key in set1: 
	set1.add("weight") # 改变了原数据大小,所以报错 
print(set1) 

set1 = {"name", "age", "height"} 
for key in set1: 
	set1.add("age") # 没有改变原数据大小,所以不报错 
print(set1)

解决思路:遍历一个新的字典/集合,不受原数据修改的影响即可

import copy 
dic = {"name": "Tom", "age": 18, "height": 188} 
dic2 = {"name": "Tom", "age": 18, "height": 188} # 思考:换成 dic2 = dic 是否可以? 
for key in dic2: 
	dic.pop(key) 
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
dic2 = dic.copy() # 浅拷贝 
for key in dic2: 
	dic.pop(key) 
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
dic2 = copy.copy(dic) # 浅拷贝 
for key in dic2: 
	dic.pop(key)
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
dic2 = copy.deepcopy(dic) # 深拷贝 
for key in dic2: 
	dic.pop(key) 
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
key_list = list(dic) # 返回一个新的对象,同理: tuple()/set()也可以 
for key in key_list: 
	dic.pop(key) 
print(dic) 

dic = {"name": "Tom", "age": 18, "height": 188} 
dict_keys = dic.keys() # 思考:这样可以吗? 
for key in dict_keys: 
	dic.pop(key) 
print(dic)

12. 函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数

12.1 自定义函数

格式:
def func_name([arg1 [, arg2, … argN]]):
func_body

函数调用
形参: 函数中声明的参数;实参: 调用时传入的参数

定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从Python提示符执行;

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋值给一个变量,相当于给这个函数起了一个别名

def plus(num): 
	print(num + 1) 
plus(1) # 函数调用 
func = plus # func和plus指向同一个内存地址 
func(1) # 函数调用 

# 定义好的函数可重复使用 
plus(2) 
func(2)

不带参数

# 定义一个不带参数的函数,输出一个由*号组成的3层三角形 
def trigon(): 
	for i in range(1, 6, 2): 
		print(("*" * i).center(5)) 
# 调用函数 
trigon()

带参数

""" 定义一个带参数的函数, sign: 构成三角形的标志,为单个字符 
layers: 三角形的层数, 为int类型 """ 
def trigon(sign, layers): 
	width = 2 * layers - 1 # 最底层的宽度 
	for i in range(1, width + 1, 2):
		print((sign * i).center(width)) 
# 调用函数 
trigon("*", 3)

12.2 return用法

返回值
函数并非总是将结果直接输出,相反,函数的调用者需要函数提供一些通过函数处理过后的一个或者一组数据,只有调用者拥有了这个数据,才能够做一些其他的操作。那么这个时候,就需要函数返回给调用者数据,这个就被称之为返回值,想要在函数中把结果返回给调用者。

return 后面可以跟单个对象,多个对象,表达式,不跟
return 把后面跟的值返回给调用方,并结束对应的函数
return语句用于退出函数,选择性的向调用者返回一个表达式。直接return的语句返回None。

def add(left, right): 
    res = left + right 
    return res # 返回单个对象 

result = add(3, 4)
print(result) 

def add(left, right): 
    return left + right # 返回表达式 

result = add(3, 4) 
print(result) 

def add(left, right): 
    res1 = left + right 
    res2 = left * right 
    return res1, res2 # 返回多个对象,把result1,result2 作为一个元组返回 
    
# def add(left, right): 
# res = left + right 
# return res, # 注意:这也是元组 (res,) 

result = add(4, 3) 
print(result) 
res1, res2 = add(4, 3) # 解包(序列赋值) 
print(res1, res2) 

def func(): 
    return 

res = func() # return后面什么也不跟, 相当于return None, 所以打印输出None print(res)

def func(): 
    print("hello") # return None 函数在没有return时,默认 return None 

print(func()) # 注意输出顺序:先输出"hello", 再输出None
def func(): 
    for i in range(5): 
        print(i) 
        return 

print(func()) 

def func(): 
    for i in range(5): 
        print(i) 
        break 
    
print(func()) 

def func(): 
    for i in range(5): 
        print(i) 
        return 
    print("ending...") 
    
print(func()) 

def func(): 
    for i in range(5): 
        print(i) 
        break 
    print("ending...")
print(func())

12.3 文档注释

def my_abs(x): 
	"""Return the absolute value of the argument.""" 
	return x if x > 0 else -x 
print(my_abs(-3)) 
# 文档注释存放在 __doc__ 属性中 
print(my_abs.__doc__) 
print(abs.__doc__) 
help(my_abs) 

def my_divmod(x, y): 
	""" Return the tuple (x//y, x%y). Invariant: div*y + mod == x. 
	x: number (except complex) y: number (except complex) """ 
	div = x // y 
	mod = x % y 
	return div, mod 
print(my_divmod.__doc__) 
help(my_divmod)

help([object])
启动内置的帮助系统
如果没有实参,解释器控制台里会启动交互式帮助系统
如果实参是一个字符串,则在模块、函数、类、方法、关键字或文档主题中搜索该字符串,并在控制台上打印帮助信息
如果实参是其他任意对象,则会生成该对象的帮助页

help() # 启动交互式帮助系统 
help("keywords") # 查看关键字 
help(list) # 生成list的帮助页

abs(x)
x:可以是整数,浮点数,布尔型,复数
返回一个数的绝对值,如果参数是一个复数,则返回它的模

print(abs(-6.9)) # 6.9,参数是整数 
print(abs(6.9)) # 6.9,参数是浮点数 
print(abs(3+4j)) # 5,参数是复数

divmod(a, b)
a:数字(非复数)
b:数字(非复数)
返回一个包含商和余数的元组 (a // b, a % b)

print(divmod(7, 3)) # (2, 1) 
print(7 // 3, 7 % 3) 

print(divmod(-9, 2)) # (-5, 1) 
print(-9 // 2, -9 % 2)

max(iterable, *[, key, default]) / max(arg1, arg2, *args[, key])
返回给定参数的最大值

print(max([1, 2, 3])) # 传入非空 iterable,返回其中的最大值,输出 3
print(max(1, 2, 3, 4)) # 传入多个数值也行,输出 4 
print(max("1", "2", "3", "10")) # 传入数字型字符串也行,不过是按照第一个字符的数值大小来比较的,输出 "3" 
print(max([], default=666)) # 传入空 iterable,返回 default指定的值,不指定则报错,输出 666 
print(max([1, 2, 3], key=lambda x: -x)) # key参数指定函数,iterable每个元素应用该函数,再执行max,输出 1

min(iterable, *[, key, default]) / min(arg1, arg2, *args[,key])
返回给定参数的最小值

print(min([1, 2, 3])) # 传入非空 iterable,返回其中的最小值,输出 1 
print(min(1, 2, 3, 4)) # 传入多个数值也行,输出 1 
print(min("2", "3", "10")) # 传入数字型字符串也行,不过是按照第一个字符的数值大小来比较的,输出 "10" 
print(min([], default=666)) # 传入空 iterable,返回default指定的值,不指定则报错,输出 666 
print(min([1, 2, 3], key=lambda x: -x)) # key参数指定函数,iterable每个元素应用该函数,再执行min,输出 3

pow(base, exp[, mod])
返回 base 的 exp 次幂;如果 mod 存在,则返回 base 的 exp 次幂对 mod 取余

print(pow(-2, 3)) # -2**3 = -8 
print(pow(-2, 3, 3)) # -2**3 % 3 = 1

round(number [, ndigits])
number:数字
ndigits:保留的小数点位数
返回 number 四舍五入到小数点后 ndigits 位精度的值,如果ndigits 被省略或为 None,则返回值为整数,否则返回值和number 类型相同

# 精确到整数位,距离 2 和 1 相同,则选择偶数2,且省略了 ndigits,所以结果为整数 2 
print(round(1.5)) 
# 精确到整数位,距离 -2 和 -1 相同,则选择偶数-2,且ndigits为 None,所以结果为整数 -2 
print(round(-1.5, ndigits=None)) 
# 精确到整数位,距离 -2 和 -1 相同,则选择偶数-2,但ndigits没有省略也没有为None,所以结果类型和number类型一样,即浮点型 -2.0 
print(round(-1.5, ndigits=0)) 
# 精确到整数位,距离 -1 和 0 相同,则选择偶数0,但ndigits没有省略也没有为None,所以结果类型和number类型一样,即浮点型 -0.0 
print(round(-0.5, ndigits=0)) 

# 精确到小数点后两位,根据距离相同选择偶数的规则,预期结果应该是 2.68,但是结果却是2.67 
# 这跟浮点数的精度有关。我们知道在机器中浮点数不一定能精确表达,因为换算成一串 1 和 0 后可能是无限位数的,机器已经做出了截断处理。
# 那么在机器中保存的2.675这个数字可能就比实际数字要小那么一点点。这一点点就导致了它离 2.67 要更近一点点,所以保留两位小数时就近似到了 2.67。 
print(round(2.675, 2)) 
# 精确到小数点后两位,根据距离相同选择偶数的规则,预期结果应该是 2.66,但是结果却是2.67。和上面同理,机器中保存的2.665可能比实际大一点点 
print(round(2.665, 2))

sum(iterable, /, start=0)
iterable:元素必需为数字类型的可迭代对象
start:累加的起始数字,默认为0
从 start 开始自左向右对 iterable 的项求和并返回总计值

print(sum([1, 2, 3], start=100)) # 100 + 1 + 2 + 3 = 106 
print(sum((1, 2, 3), start=100)) # 106 
print(sum({1, 2, 3}, start=100)) # 106 
print(sum({1: "name", 2: "age", 3: "address"}, start=100)) # 106

12.4 类型标注

Python 对标注类型并不强制,不按照标注类型也不会报错
类型标注主要被用于第三方工具,比如类型检查器、集成开发环境、静态检查器等
标注以字典的形式存放在函数的 annotations 属性中,并且不会影响函数的任何其他部分
自从 Python3.5 以来,发布了 typing 包,为类型标注提供了支持

def func(a: int, b: str, c: list, d: tuple, e: dict, f: set) -> tuple:
	return a, b, c, d, e, f 
print(func(1, 2, 3, 4, 5, 6)) 
print(func(1, "2", [3], (4, ), {5: 5}, {6})) 
print(func.__annotations__) 

from typing import List, Tuple, Dict, Set, Union, Callable, Iterable, Iterator, Generator 
# List[int]:建议参数为列表,且所有元素为int类型 
# Tuple[int, str]:建议参数为元组,且第一个元素为int类型,第二个元素为str类型
# Dict[str, int]:建议参数为字典,且所有键为str类型,值为int类型
# Set[str]:建议参数为集合,且所有元素为str类型 
def func(a: int, b: str, c: List[int], d: Tuple[int, str], e: Dict[str, int], f: Set[str]) -> Tuple: 
	return (a, b, c, d, e, f) 
print(func(1, 2, 3, 4, 5, 6)) 
print(func(1, "2", [3, 4, 5], (4, "5"), {"5": 4}, {"6"})) 

# 嵌套 
def func(a: Dict[str, List[Tuple[int, int, int]]]): 
	pass 

# 建议多种类型 
def func(a: Union[str, list, set]): 
	pass 
func() 
func([1]) 
func("1") 
func({1}) # Callable[[int], int] 建议参数为函数,且只有一个为int类型的参数,返回int类型 
def my_sort(a: list, f: Callable[[int], int], c: bool):
	return sorted(a, key=f, reverse=c) 
print(my_sort([1, -3, 2], abs, True))

12.5 参数传递

传不可变对象 & 传可变对象
不可变对象,传递的是对象的值,不是对象本身,如果修改值,修改的是另一个复制的对象,不会影响原来对象的本身;
可变对象,传递对象自己。函数内部如果修改值会影响对象本身。

def func(b): 
	print(id(a), a) 
	print(id(b), b) 
	b = 888 
	print(id(a), a) 
	print(id(b), b) 
a = 999 
func(a) 

def func(b): 
	print(id(a), a) 
	print(id(b), b) 
	b = [999, 888, 777] 
	print(id(a), a) 
	print(id(b), b) 
a = [999, 777] 
func(a) 

def func(b): 
	print(id(a), a) 
	print(id(b), b) 
	b.insert(1, 888) 
	print(id(a), a) 
	print(id(b), b) 
a = [999, 777] 
func(a)

12.6 参数分类

必需参数
必须传参, 否则报错(实参按照形参的对应位置顺序传入,所以通常也叫:位置参数)

def func(a, b): 
	print(a - b) 
func(4, 3) # 1 
func(3, 4) # -1

关键字参数
允许实参和形参顺序不一致,因为是通过关键字确定传入的值

def info(name, age): 
	print('姓名:', name) 
	print('年龄:', age) 
info(age=18, name='小明') 
info(name='小明', age=18)

默认参数
默认参数要写在非默认参数的后面,不能写在**kwargs参数的后面
调用函数时,如果该参数没有指定实参,则会使用默认值

def info(name, age=18): 
	print("姓名:", name) 
	print("年龄:", age) 
info("小明") 
info("小明", age=9)

不定长参数
*args:将参数打包成元组给函数体调用,没有值传给它,就是个空元组

def func(a, b, *args): 
	print(a, b, args)
 """ a,b是必需参数,分别对应1,2 *args为不定长参数,没有值传给它,
 就是个空元组 """ 
 func(1, 2) 
 """ a,b是必需参数,分别对应1,2 *args为不定长参数,
 把剩下的3,4打包成元组 """ 
 func(1, 2, 3, 4)

**kwargs:将参数打包成字典给函数体调用,没有值传给它,就是个空字典

def func(a, b, **kwargs): 
	print(a, b, kwargs) 
"""a,b是必需参数,分别对应1,2 **kwargs为不定长参数,没有值传给它,
就是个空字典 """ 
func(1, 2) 
""" a,b是必需参数,分别对应1,2 **kwargs为不定长参数,把关键字形式的
参数打包成字典 """ 
func(1, 2, name="Tom", age=18)

参数顺序固定:当必需参数,*arg参数,**kwargs参数同时存在时,它们的顺序是固定的,否则报错(必需参数 ->*arg参数 -> **kwargs参数)

# 先是a,b, 再是*args, 最后**kwargs 
def func(a, b, *args, **kwargs): 
	print(a, b, args, kwargs) 
func(1, 2, 3, 4, name="Tom", age=18)

特殊参数
默认情况下,函数的参数传递形式可以是位置参数或是显式的关键字参数
也可以用 / 和 * 来限制参数的传递形式
其中 / 为仅限位置参数,限制在它之前的形参必须以位置参数的形式传入,而不能用关键字参数
其中 * 为仅限关键字参数,限制在它之后的形参必须以关键字参数的形式传入
这两个特殊参数只是为了限制参数的传递形式,不需要为它们传入实参

def func(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): 
	pass 
func(1, 2, 3, kwd1=4, kwd2=5)

12.7 匿名函数

定义函数的过程中,没有给定名称的函数就叫做匿名函数;Python中使用lambda表达式来创建匿名函数

格式:
lambda [arg1 [, arg2, … argN]] : expression

匿名函数的参数可以有多个, 但是后面的 expression 只能有一个
匿名函数返回值就是 expression 的结果,而不需要 return 语句
匿名函数可以在需要函数对象的任何地方使用(如:赋值给变量、作为参数传入其他函数等),因为匿名函数可以作为一个表达式,而不是一个结构化的代码块

# 定义了一个没有参数的匿名函数,返回一个字符串 
# 用标识符func1指向这个匿名函数的内存地址 
func1 = lambda : "It just returns a string" 
print(func1()) # 调用匿名函数,输出函数的返回值 

# 定义了一个含有三个参数的匿名函数,没有返回值
 # 用标识符func2指向这个匿名函数的内存地址 
 func2 = lambda x, y, z: print(x + y + z) 
 func2(1, 2, 3) # 调用匿名函数,传入对应实参 

# 把匿名函数作为参数传入其他自定义函数 
def call_f1(function): 
	print(function()) 
call_f1(func1) 
call_f1(lambda : "It just returns a string")

# 把匿名函数作为参数传入其他自定义函数 
def call_f2(function, a, b, c): 
	function(a, b, c) 
call_f2(func2, 1, 2, 3) 
call_f2(lambda x, y, z: print(x + y + z), 1, 2, 3) 

# 把匿名函数作为参数传入其他匿名函数 
func2 = lambda x, y, z: print(x + y + z) 
func3 = lambda function, a, b, c: function(a, b, c) 
func3(func2, 1, 2, 3) 

# 把匿名函数作为参数传入其他内置函数 
print(sorted([-4, 2, 3], key=lambda x: -x if x < 0 else x))

lambda 来创建匿名函数规则:
● lambda只是一个表达式,函数体比def简单很多。
● lambda的主体是一个表达式,而不是一个代码块,所以不能写太多的逻辑进去。
● lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
● lambda定义的函数的返回值就是表达式的返回值,不需要return语句块
● lambda表达式的主要应用场景就是赋值给变量、作为参数传入其它函数

13. 封包、解包、迭代器、生成器

13.1 封包

将多个值赋值给一个变量时,Python 会自动将这些值封装成元
组,这个特性称之为封包

a = 1, 2, 3, 4 
print(a)

13.2 解包

可迭代对象都支持解包

赋值过程中的解包:

# 赋值符号左边变量和右边可迭代对象的元素数量一样,则一一对应赋值 
a, b, c, d = (1, 2, 3, 4) 
print(a, b, c, d) # 1 2 3 4 

# 当左边和右边数量不一样时,会报错 
a, b, c, d = (1, 2, 3, 4, 5) 
print(a, b, c, d) # 报错 

""" 可以在其中一个变量前面加一个星号(*),代表这个变量可接收0个/1个/ 多个元
素,并把它们组成列表来赋值,理解起来类似于不定长参数中的 *args。解包过程:先
把其他变量根据位置确定对应要赋值的元素,剩下的 元素都归带星号(*)的变量,组成
列表来赋值 """
a, b, *c, d = (1, 2, 3, 4, 5) 
print(a, b, c, d) # 1 2 [3, 4] 5 

a, b, *c, d, e = (1, 2, 3, 4, 5) 
print(a, b, c, d, e) # 1 2 [3] 4 5 

a, b, *c, d, e = (1, 2, 3, 4) 
print(a, b, c, d, e) # 1 2 [] 3 4 

# 不允许多个带星号(*)的变量,因为会有歧义 
a, *b, *c, d = (1, 2, 3, 4, 5) 
print(a, b, c, d)
# 这种解包的写法是错误的 
*a = (1, 2, 3, 4, 5)
# 正确的写法应该是这样 
*a, = (1, 2, 3, 4, 5)
print(a) # [1, 2, 3, 4, 5] 

# 通常约定:当变量不需要使用时, 可以用下划线命名 
*a, _, _ = (1, 2, 3, 4, 5) 
print(a) # [1, 2, 3]

在可迭代对象前面加一个星号(*),在字典对象前面加双星(**),这种解包方式主要运用在函数传参的过程中

a = (1, 2, 3, 4, 5) 
print(*a) # 1 2 3 4 5 
print(*(1, 2, 3, 4, 5)) # 同上 
""" 思考:下面两个 *a 是一样的吗? 
不一样。两个星号(*)的作用是不一样的, 第一个*a中的星号代表变量a可以
接收0个/1个/多个元素,从而得到a为[1, 2, 3],而第二个*a中的星号代表
解包,对变量a解包,即对[1, 2, 3]解包,即 *[1, 2, 3] """ 
*a, b, c = (1, 2, 3, 4, 5) 
print(*a) # 1 2 3 

# 在可迭代对象前面加一个星号(*),使其以位置参数的形式传入函数 
def func(a, b, c, d=None): 
	print(a, b, c, d) 
tup = (1, 2, 3, 4) 
dic = {'name': "Tom", 'age': 18, 'height': 188} 
func(*tup) # 等效于 func(1, 2, 3, 4) 
func(*dic) # 等效于 func('name', 'age', 'height') 

# 在字典对象前面加双星(**),使其以关键字参数的形式传入函数 
def func(a, b, c, d=None): 
	print(a, b, c, d)
dic = {'a': "Tom", 'b': 18, 'c': 188, 'd': True} 
func(**dic) # 等效于 func(a="Tom", b=18, c=188, d=True) 
dict(**dic) # 等效于 dict(a="Tom", b=18, c=188, d=True)

13.3 迭代器

迭代器是一个可以记住遍历的位置的对象。 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器只能往前不会后退。
迭代器有两个基本的方法:iter()(可理解为一个类型## 标题) 和 next()。
字符串,列表或元组,集合对象都可用于创建迭代器:

iter(iterable) -> iterator
iter(callable, sentinel) -> iterator
从一个对象中获取迭代器。
形式一:必须提供迭代器参数,或者是序列参数;
形式二:调用可调用对象,直到返回sentinel。

endstr = '#'
str1 = ''
print('请输入:(按#号结束输入)')
for line in iter(input, endstr):
    str1 += line.upper()+'\n'
print(str1)

next(iterator[, default])
从迭代器返回下一项。如果给定默认值default并且迭代器已用尽,则返回该迭代器,而不触发 StopIteration 报错。

list=[1,2] 
it = iter(list) # 创建迭代器对象
print (next(it)) # 输出迭代器的下一个元素 1
print (next(it))  # 2
print (next(it,3))  # 3

迭代器对象,访问里面的元素的时候, 是不是更耗时间呢? 是的,但是省空间

lst=[1,2,3,4,5,6,7]*1000000
import sys
print('list的内存大小为:%d'%(sys.getsizeof(lst)))  # 56000056
it = iter(lst) # 创建迭代器对象
print('迭代器的内存大小为:%d'%(sys.getsizeof(it)))  # 48
print (next(it)) # 输出迭代器的下一个元素 1
print (next(it))

13.4 生成器

使用了 yield 的函数被称为生成器。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。并在下一次从当前位置继续运行。

def f():
    print('start')

    a = yield 1
    print(a)
    print('middle....')
    b = yield 2 # 2这个值只是迭代值,调用next时候返回的值
    print(b)
    print('next')
    c = yield 3
    print(c)
a = f()
print(a)  # 
print(next(a))  # start 1
print(next(a))  # None middle....
print(next(a))  # # None next

# 生成器
def myfunc():
    print('start')
    # return # 意味着函数结束
    yield 1
    print('middle....')
    yield 2 # 2这个值只是迭代值,调用next时候返回的值
    print('next')
    yield 3

a = myfunc() # 并不会执行函数体,只是得到一个生成器,相当于myfunc的别名
# 通过next方法来执行函数体,遇到yield的时候,得到yield后面的结果,同时退出函数
# 如果直到函数结束都遇不到yield,则会报StopIteration错
print(next(a))
print(next(a))
print(next(a))

yield 的作用就是把一个函数变成一个generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个生成器,如调用func函数,不会执行该函数,而是返回一个iterable迭代对象!

def fib(max):
    n,a,b =0,0,1
    while n < max:
        yield b
        a,b =b,a+b
        n = n+1
    return 'done'
for i in fib(6):
    print(i)

区别:与return类似,都可以返回值,但不一样的地方,yield可以返回多次值,而return只能返回一次。

#应用场景:加载文件,库里的数据的时候,结合循环来获取数据的时候
def myfunc2():
    print('开始生成数据:')
    for i in range(3):
        print(f'第{i+1}个数据:')
        yield i
    print('end')
b = myfunc2()
# print(next(b))  # 开始生成数据:第1个数据: 0
# print(next(b))  # 第2个数据: 1
# print(next(b))  # 第3个数据: 2
#print(next(b))#  StopIteration ,需要自己捕获异常

for j in b:# 隐式调用next(b), j=next(b) #安全访问,不会抛出这个异常StopIteration
    print(j)
# 开始生成数据:
# 第1个数据:
# 0
# 第2个数据:
# 1
# 第3个数据:
# 2
# end (无异常)
    
#for .. in ...遇到StopIteration就停止迭代,但是并不会抛出这个异常StopIteration

你可能感兴趣的:(python,深度学习,机器学习,python,学习,开发语言)