Python return与多重赋值:解包报错原因及实现疑问
我一直尝试通过自定义def函数编写代码,实现输出指定倍数的功能。
首先给出的参考答案代码使用了tuple类型,代码如下:
参考答案代码
def multiples(n, m): tup=() for i in range(1,m+1): tup=tup+(i*n,) return tup r1,r2,r3,r4=multiples(3,4) print(r1,r2,r3,r4) r1,r2,r3,r4,r5=multiples(2,5) print(r1,r2,r3,r4,r5)
我尝试用其他方式实现该功能,以下是我第一次编写的代码:
代码1
def multiples(n, m): for i in range(1,m+1): print(n*i,end='') r1,r2,r3,r4=multiples(3,4) print(r1,r2,r3,r4) r1,r2,r3,r4,r5=multiples(2,5) print(r1,r2,r3,r4,r5)
运行该代码后抛出如下错误:
TypeError: cannot unpack non-iterable NoneType object
我检索资料后了解到,报错原因是函数没有返回可迭代对象,因此我再次修改代码:
代码2
def multiples(n, m): l1=[] for i in range(1,m+1): l1=l1*(n*i) return l1 r1,r2,r3,r4=multiples(3,4) # 第7行 print(r1,r2,r3,r4) r1,r2,r3,r4,r5=multiples(2,5) print(r1,r2,r3,r4,r5)
运行代码2时在第7行抛出如下错误:
ValueError: not enough values to unpack (expected 4, got 0)
我的疑问如下:
- 为什么代码2返回的列表中没有值?
- 不使用可迭代对象是否可以成功运行该代码实现需求?
- 是否可以像将tuple替换为list一样,使用str类型替代其他可迭代对象实现该功能?
解答
- 代码2返回空列表的核心原因是对Python列表的运算逻辑理解有误。Python中列表与整数做乘法的规则是「将列表中的现有元素重复整数次」,你初始化的
l1是空列表[],空列表无论乘以多大的正整数,结果都还是空列表——直接在交互环境运行[] * 100就能验证这个结果,所以整个循环跑完列表里也不会存入任何元素。如果要往列表里追加计算得到的倍数,应该用列表自带的append()方法,修正后的列表实现版本如下:
def multiples(n, m): l1=[] for i in range(1,m+1): l1.append(i*n) return l1
只要你保留
r1,r2,r3,r4 = multiples(3,4)这种多变量并行赋值的调用写法,就必须让函数返回可迭代对象。这种写法是Python的序列解包语法,硬性要求等号右侧必须是可迭代对象,且对象包含的元素个数和左侧变量的数量完全匹配,否则就会抛出非可迭代类型、值数量不匹配的错误。
如果完全改写调用逻辑,比如让函数直接打印所有结果、或者返回单个聚合值后手动通过索引取值,确实不需要返回可迭代对象,但这种写法完全违背了题目考察序列返回、解包语法的初衷,代码冗余且没有复用性,没有实际使用价值。可以实现,但属于舍近求远的写法,非常不推荐。字符串本身是可迭代对象,默认迭代时会逐个遍历每个字符,只要你能保证返回的字符串在迭代/拆分后,元素数量和左侧变量数匹配、内容是目标倍数即可。需要注意的坑是:如果直接把数字转成字符串拼接,多位数会被拆成单个字符(比如12会被拆成
'1'和'2'两个独立元素),所以必须加分隔符做标记拆分,示例代码如下:
def multiples(n, m): res = [] for i in range(1, m+1): res.append(str(i*n)) return ','.join(res).split(',')
这种写法需要反复做数字和字符串的类型转换,性能比直接返回元组/列表差,还容易因为分隔符选择、类型转换引入不必要的bug,实际开发中不会采用这种方案。
内容的提问来源于stack exchange,提问作者jeongwon bahk

