【二十六】列表、字典、集合迭代问题和函数

列表、字典、集合迭代问题和函数

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

列表内存自动管理

思考:下面程序的输出结果是什么?

lis = [1, 2, 3]

for item in lis:
    lis.remove(item)
    
print(lis) # [2]

为什么没有得到空列表?

如果原列表在内存中为:

image-20230228095308354

for 循环是按照索引来遍历元素的,第一次循环取索引0对应的元素 (即元素1),然后再执行 remove() 方法删除它,此时列表内存会进行收缩,使得后面的元素2、3都往前移动,则列表在内存中变为:

image-20230228095420001

而因为内存收缩就导致了第二次循环取索引1对应的元素时就取到 了元素3,然后再执行 remove() 方法删除它,列表就变为:

image-20230228095443017

列表内存自动管理功能: 在删除列表中的元素时,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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值