为什么我像这样使用 for 循环但仍然得到 IndexError?
Why I use for loop like this but still get an IndexError?
我一直认为如果我这样使用for循环:
for line in lines:
dosomething()
我永远不会让我的列表索引超出范围。然而,我错了..
我的代码的目的是输出输入集的所有子集。
示例:
输入:
[2,1,3]
输出:
[[],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]]
代码如下:
def bin_list(bit_len, list_len):
# To get a binary numbers list like this:
# ['000', '001', '010', '011', '100', '101', '110', '111']
list_Bin = [None] * list_len
for i in range(list_len):
extra_d = bit_len-len(bin(i)[2:])
list_Bin[i] = '0'*extra_d + bin(i)[2:]
return list_Bin
def subsets(nums):
nums.sort()
max_element = len(nums)
size_subset = 2 ** max_element
list_Bin = bin_list(max_element, size_subset)
list_Result = [[None]*max_element for i in range(size_subset)]
for i, Bin in enumerate(list_Bin):
for j in range(max_element):
if Bin[j] == '1':
#1 list_Result[i][j] = nums[i]
#2 list_Result[i].append(nums[i])
print(list_Result)
nums = [1,2,3]
subsets(nums)
所以我的核心概念是使用二进制列表来创建子集列表。
例如,当我在二进制列表中读取 001 时。我会
保留3,因为第三位是1
由于第一位和第二位均为0,因此删除1、2。
因此,如果我使用 0-7(二进制 == 000-111),我可以获得 [1,2,3] 的所有子集。
但我总是得到 IndexError: list index out of range
。不管我用
#1 list_Result[i][j] = nums[i]
或
#2 list_Result[i].append(nums[i])
由于以下原因您收到错误消息:
len(nums) == 3
size_subset == 2**3 == 8
nums[3]
在 i
从 0
到 len[list_Bin]
的循环中被访问。
解决方案可能是访问 nums[j]
而不是 nums[i]
,因为 j
从 0
变为 len(nums)
。
您 IndexError
的原因是您混淆了索引。在 subsets
函数中的嵌套 for
循环中,i
范围超过子集的数量,j
范围超过 nums
集中的元素数量。但是当你 应该 做 nums[j]
时你尝试访问 nums[i]
,所以当 i
大于或等于 len(nums)
你将尝试访问超出 nums
列表末尾的内容,因此 IndexError
。
您的代码可以稍微简化。无需使用 bin
函数:您可以告诉 format
以所需长度格式化二进制整数。
def bin_list(bit_len):
list_len = 2 ** bit_len
return ['{0:0{1}b}'.format(i, bit_len) for i in range(list_len)]
def subsets(seq):
all_subsets = []
for bits in bin_list(len(seq)):
s = [seq[i] for i, b in enumerate(bits) if b == '1']
all_subsets.append(s)
return all_subsets
print(subsets([1,2,3]))
print(subsets('ABCD'))
输出
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
[[], ['D'], ['C'], ['C', 'D'], ['B'], ['B', 'D'], ['B', 'C'], ['B', 'C', 'D'], ['A'], ['A', 'D'], ['A', 'C'], ['A', 'C', 'D'], ['A', 'B'], ['A', 'B', 'D'], ['A', 'B', 'C'], ['A', 'B', 'C', 'D']]
我承认 '{0:0{1}b}'.format(i, bit_len)
有点晦涩难懂,所以我会尽可能简单地解释它,自由引用 Python 3 Format String Syntax 的官方文档].
表达式 '{0:0{1}b}'.format(i, bit_len)
创建一个字符串,其中包含 i
的二进制表示形式。创建的字符串为 bit_len
个字符宽,如有必要,在左侧用零填充。
'{0:0{1}b}'
部分称为格式字符串。格式字符串包含由花括号 {}
包围的“替换字段”。这是一个复杂的示例,因为它有一个替换字段嵌套在另一个替换字段中。
在替换字段中,紧跟在 {
之后的项目称为 field_name。它用于指定 .format
的哪个参数与此替换字段相关联。 field_name 可以是参数的名称、位置,也可以省略(在 Python 2.7 及更高版本中)并且 Python 将仅按数字顺序使用 .format
参数当它与替换字段匹配时。
field_name 之后可选地跟一个转换字段,前面有一个感叹号 !
,和一个 format_spec (格式规范),前面有一个冒号:
。这些为替换值指定了非默认格式。此特定格式字符串没有转换字段,因此使用默认转换。
A format_spec 以一个字母结尾,它指定了我们想要的格式类型。我们的 format_spec 以 b
结尾,表示我们要将整数格式化为二进制。 b
之前的数字指定了结果位串的宽度;通过在其前面放置一个零,我们表示我们希望用零而不是空格填充字符串。因此 08b
的 format_spec 会为我们提供 8 个字符宽的位串(或者如果需要更宽以正确表示参数),短位串用零填充。
但我们需要比这更奇特的东西,因为我们没有固定的位串宽度 - 我们希望我们的位串为 bit_len
个字符宽。但这没关系,因为格式语法允许我们将一个替换字段放在另一个替换字段中!因此,我们有 0{1}b
而不是 08b
,现在位置 1 中的参数值用作位串宽度。
前面提到field_name可以是参数名,所以我们原来的格式表达式也可以写成:
'{val:0{width}b}'.format(val=i, width=bit_len)
我想这个表格比原来的更易读。 :)
我还提到可以省略 field_name(在 Python 2.7 及更高版本中),它看起来像这样:
'{:0{}b}'.format(i, bit_len)
但我不建议这样做,因为虽然它更短,但比原来的更神秘,恕我直言。
然而,我们并不真正需要 bin_list
中的位串:我们可以只在子集的索引上使用按位运算符。
def subsets(seq):
bit_len = len(seq)
bitrange = range(bit_len)
all_subsets = []
for j in range(1 << bit_len):
s = [seq[i] for i in bitrange if j & (1<<i)]
all_subsets.append(s)
return all_subsets
print(subsets([1,2,3]))
print(subsets('ABCD'))
输出
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
[[], ['A'], ['B'], ['A', 'B'], ['C'], ['A', 'C'], ['B', 'C'], ['A', 'B', 'C'], ['D'], ['A', 'D'], ['B', 'D'], ['A', 'B', 'D'], ['C', 'D'], ['A', 'C', 'D'], ['B', 'C', 'D'], ['A', 'B', 'C', 'D']]
这确实会以不同的顺序生成子集,但更改它并不难,而且我认为这种顺序更自然。
我们可以通过使用嵌套列表理解使上一个版本更加紧凑:
def subsets(seq):
bit_len = len(seq)
bitrange = range(bit_len)
return [[seq[i] for i in bitrange if j & (1<<i)]
for j in range(1 << bit_len)]
FWIW,这是制作子集的另一种方法。效率有点低,但是对于小的输入序列还是可以的。它按照与以前版本相同的顺序生成子集。
def subsets(seq):
z = [[]]
for x in seq:
z += [y + [x] for y in z]
return z
我一直认为如果我这样使用for循环:
for line in lines:
dosomething()
我永远不会让我的列表索引超出范围。然而,我错了..
我的代码的目的是输出输入集的所有子集。
示例:
输入:
[2,1,3]
输出:
[[],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]]
代码如下:
def bin_list(bit_len, list_len):
# To get a binary numbers list like this:
# ['000', '001', '010', '011', '100', '101', '110', '111']
list_Bin = [None] * list_len
for i in range(list_len):
extra_d = bit_len-len(bin(i)[2:])
list_Bin[i] = '0'*extra_d + bin(i)[2:]
return list_Bin
def subsets(nums):
nums.sort()
max_element = len(nums)
size_subset = 2 ** max_element
list_Bin = bin_list(max_element, size_subset)
list_Result = [[None]*max_element for i in range(size_subset)]
for i, Bin in enumerate(list_Bin):
for j in range(max_element):
if Bin[j] == '1':
#1 list_Result[i][j] = nums[i]
#2 list_Result[i].append(nums[i])
print(list_Result)
nums = [1,2,3]
subsets(nums)
所以我的核心概念是使用二进制列表来创建子集列表。
例如,当我在二进制列表中读取 001 时。我会
保留3,因为第三位是1
由于第一位和第二位均为0,因此删除1、2。
因此,如果我使用 0-7(二进制 == 000-111),我可以获得 [1,2,3] 的所有子集。
但我总是得到 IndexError: list index out of range
。不管我用
#1 list_Result[i][j] = nums[i]
或
#2 list_Result[i].append(nums[i])
由于以下原因您收到错误消息:
len(nums) == 3
size_subset == 2**3 == 8
nums[3]
在i
从0
到len[list_Bin]
的循环中被访问。
解决方案可能是访问 nums[j]
而不是 nums[i]
,因为 j
从 0
变为 len(nums)
。
您 IndexError
的原因是您混淆了索引。在 subsets
函数中的嵌套 for
循环中,i
范围超过子集的数量,j
范围超过 nums
集中的元素数量。但是当你 应该 做 nums[j]
时你尝试访问 nums[i]
,所以当 i
大于或等于 len(nums)
你将尝试访问超出 nums
列表末尾的内容,因此 IndexError
。
您的代码可以稍微简化。无需使用 bin
函数:您可以告诉 format
以所需长度格式化二进制整数。
def bin_list(bit_len):
list_len = 2 ** bit_len
return ['{0:0{1}b}'.format(i, bit_len) for i in range(list_len)]
def subsets(seq):
all_subsets = []
for bits in bin_list(len(seq)):
s = [seq[i] for i, b in enumerate(bits) if b == '1']
all_subsets.append(s)
return all_subsets
print(subsets([1,2,3]))
print(subsets('ABCD'))
输出
[[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
[[], ['D'], ['C'], ['C', 'D'], ['B'], ['B', 'D'], ['B', 'C'], ['B', 'C', 'D'], ['A'], ['A', 'D'], ['A', 'C'], ['A', 'C', 'D'], ['A', 'B'], ['A', 'B', 'D'], ['A', 'B', 'C'], ['A', 'B', 'C', 'D']]
我承认 '{0:0{1}b}'.format(i, bit_len)
有点晦涩难懂,所以我会尽可能简单地解释它,自由引用 Python 3 Format String Syntax 的官方文档].
表达式 '{0:0{1}b}'.format(i, bit_len)
创建一个字符串,其中包含 i
的二进制表示形式。创建的字符串为 bit_len
个字符宽,如有必要,在左侧用零填充。
'{0:0{1}b}'
部分称为格式字符串。格式字符串包含由花括号 {}
包围的“替换字段”。这是一个复杂的示例,因为它有一个替换字段嵌套在另一个替换字段中。
在替换字段中,紧跟在 {
之后的项目称为 field_name。它用于指定 .format
的哪个参数与此替换字段相关联。 field_name 可以是参数的名称、位置,也可以省略(在 Python 2.7 及更高版本中)并且 Python 将仅按数字顺序使用 .format
参数当它与替换字段匹配时。
field_name 之后可选地跟一个转换字段,前面有一个感叹号 !
,和一个 format_spec (格式规范),前面有一个冒号:
。这些为替换值指定了非默认格式。此特定格式字符串没有转换字段,因此使用默认转换。
A format_spec 以一个字母结尾,它指定了我们想要的格式类型。我们的 format_spec 以 b
结尾,表示我们要将整数格式化为二进制。 b
之前的数字指定了结果位串的宽度;通过在其前面放置一个零,我们表示我们希望用零而不是空格填充字符串。因此 08b
的 format_spec 会为我们提供 8 个字符宽的位串(或者如果需要更宽以正确表示参数),短位串用零填充。
但我们需要比这更奇特的东西,因为我们没有固定的位串宽度 - 我们希望我们的位串为 bit_len
个字符宽。但这没关系,因为格式语法允许我们将一个替换字段放在另一个替换字段中!因此,我们有 0{1}b
而不是 08b
,现在位置 1 中的参数值用作位串宽度。
前面提到field_name可以是参数名,所以我们原来的格式表达式也可以写成:
'{val:0{width}b}'.format(val=i, width=bit_len)
我想这个表格比原来的更易读。 :)
我还提到可以省略 field_name(在 Python 2.7 及更高版本中),它看起来像这样:
'{:0{}b}'.format(i, bit_len)
但我不建议这样做,因为虽然它更短,但比原来的更神秘,恕我直言。
然而,我们并不真正需要 bin_list
中的位串:我们可以只在子集的索引上使用按位运算符。
def subsets(seq):
bit_len = len(seq)
bitrange = range(bit_len)
all_subsets = []
for j in range(1 << bit_len):
s = [seq[i] for i in bitrange if j & (1<<i)]
all_subsets.append(s)
return all_subsets
print(subsets([1,2,3]))
print(subsets('ABCD'))
输出
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
[[], ['A'], ['B'], ['A', 'B'], ['C'], ['A', 'C'], ['B', 'C'], ['A', 'B', 'C'], ['D'], ['A', 'D'], ['B', 'D'], ['A', 'B', 'D'], ['C', 'D'], ['A', 'C', 'D'], ['B', 'C', 'D'], ['A', 'B', 'C', 'D']]
这确实会以不同的顺序生成子集,但更改它并不难,而且我认为这种顺序更自然。
我们可以通过使用嵌套列表理解使上一个版本更加紧凑:
def subsets(seq):
bit_len = len(seq)
bitrange = range(bit_len)
return [[seq[i] for i in bitrange if j & (1<<i)]
for j in range(1 << bit_len)]
FWIW,这是制作子集的另一种方法。效率有点低,但是对于小的输入序列还是可以的。它按照与以前版本相同的顺序生成子集。
def subsets(seq):
z = [[]]
for x in seq:
z += [y + [x] for y in z]
return z