列表、字典、集合迭代问题和函数
列表、字典、集合迭代问题
列表内存自动管理
思考:下面程序的输出结果是什么?
lis = [1, 2, 3]
for item in lis:
lis.remove(item)
print(lis) # [2]
为什么没有得到空列表?
如果原列表在内存中为:
for 循环是按照索引来遍历元素的,第一次循环取索引0对应的元素 (即元素1),然后再执行 remove() 方法删除它,此时列表内存会进行收缩,使得后面的元素2、3都往前移动,则列表在内存中变为:
而因为内存收缩就导致了第二次循环取索引1对应的元素时就取到 了元素3,然后再执行 remove() 方法删除它,列表就变为:
列表内存自动管理功能: 在删除列表中的元素时,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) # []
字典、集合遍历问题
字典、集合在遍历时,如果改变原数据的size,则会造成迭代时报 错
dic = {"name": "Tom", "age": 18, "height": 188}
for key in dic:
dic.pop(key) # 改变了原数据大小,所以报错 # RuntimeError: dictionary changed size during iteration
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: # dictionary changed size during iteration, dict_keys是一个视图对象,可迭代对象,当原数据改变任然会受到相同的影响。
dic.pop(key)
print(dic)
函数
自定义函数
格式: def func_name([arg1 [, arg2, … argN]]):
func_body
函数调用
- 形参: 函数中声明的参数;实参: 调用时传入的参数
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)
return 用法
- return 后面可以跟单个对象,多个对象,表达式,不跟
- return 把后面跟的值返回给调用方,并结束对应的函数
def add(left, right):
res = left + right
return res # 返回单个对象
result = add(3, 4)
print(result) # 7
def add(left, right):
return left + right # 返回表达式
result = add(3, 4)
print(result) # 7
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) # (7, 12)
res1, res2 = add(4, 3) # 解包(序列赋值)
print(res1, res2) # 7 12
def func():
return
res = func() # return后面什么也不跟, 相当于return None, 所以打印输出None
print(res) # None
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())
# 0
# 1
# 2
# 3
# 4
# None
def func():
for i in range(5):
print(i)
break
print(func())
# 0
# None
def func():
for i in range(5):
print(i)
return
print("ending...")
print(func())
# 0
# None
文档注释
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)) # 6,参数是整数
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=abs)) # key参数指定函数,
iterable每个元素应用该函数,再执行max,输出 -3
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=abs)) # key参数指定函数,
iterable每个元素应用该函数,再执行min,输出 1
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 类型相同
# 精确到整数位,距离 0 和 1 相同,则选择偶数0,且省略了ndigits,所以结果为整数 0
print(round(0.5))
# 精确到整数位,距离 -1 和 0 相同,则选择偶数0,但ndigits没有省略也没有为None,所以结果类型和number类型一样,即浮点型 -0.0
print(round(-0.5, ndigits=0))
# 精确到整数位,距离 -2 和 -1 相同,则选择偶数-2,且ndigits为None,所以结果为整数 -2
print(round(-1.5, ndigits=None))
# 精确到整数位,距离 0 和 1 相同,则选择偶数0,且省略了
ndigits,所以结果为整数 0
print(round(0.5))
# 精确到整数位,距离 -1 和 0 相同,则选择偶数0,但ndigits没有省
略也没有为None,所以结果类型和number类型一样,即浮点型 -0.0
print(round(-0.5, ndigits=0))
# 精确到整数位,距离 -2 和 -1 相同,则选择偶数-2,且ndigits为None,所以结果为整数 -2
print(round(-1.5, ndigits=None))
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
类型标注
- 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))
参数传递
- 传不可变对象 & 传可变对象
# 当不可变对象和可变对象传入函数中,被赋新值,它们的id都会改变。
def func(b):
print(id(a), a) # 1681833599248 999
print(id(b), b) # 1681833599248 999
b = 888
print(id(a), a) # 1681833599248 999
print(id(b), b) # 1681772251440 888
a = 999
func(a)
def func(b):
print(id(a), a) # 2350073380736 [999, 777]
print(id(b), b) # 2350073380736 [999, 777]
b = [999, 888, 777]
print(id(a), a) # 2350073380736 [999, 777]
print(id(b), b) # 2350073735296 [999, 888, 777]
a = [999, 777]
func(a)
# 但当可变对象传递到函数中后,作为形参进行原地操作,会影响到原参数(实参)。
def func(b):
print(id(a), a) # 2027162631360 [999, 777]
print(id(b), b) # 2027162631360 [999, 777]
b.insert(1, 888)
print(id(a), a) # 2027162631360 [999, 888, 777]
print(id(b), b) # 2027162631360 [999, 888, 777]
a = [999, 777]
func(a)