为什么我像这样使用for循环但仍然得到一个IndexError?

时间:2016-03-08 04:09:33

标签: python list for-loop python-3.5

我一直以为如果我像这样使用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时。我会

由于第三位为1 ,

保持3

由于第一位和第二位为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])

2 个答案:

答案 0 :(得分:3)

由于以下原因,您收到错误消息:

  1. len(nums) == 3
  2. size_subset == 2**3 == 8
  3. 正在nums[3]i转到0的循环中访问
  4. len[list_Bin]
  5. 解决方案可能是访问nums[j]而不是nums[i],因为j0转到len(nums)

答案 1 :(得分:3)

IndexError的原因是你的指数混乱了。在for函数subsets中的嵌套i循环中,子集数量范围超过j范围内nums范围内的nums[i]范围。但是,当 正在执行nums[j]时,您尝试访问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)

我承认'{0:0{1}b}'.format(i, bit_len)有点神秘,所以我会尝试尽可能简单地解释它,从Format String Syntax的官方Python 3文档中自由引用。

表达式i创建一个包含bit_len二进制表示的字符串。创建的字符串宽'{0:0{1}b}'个字符,如果需要,在左边填充零。

{}部分称为格式字符串。格式字符串包含由大括号{包围的“替换字段”。这是一个复杂的例子,因为它有一个嵌套在另一个替换字段中的替换字段。

在替换字段中,紧跟.format之后的项称为field_name。它用于指定.format的哪个参数与此替换字段相关联。 field_name可以是参数的名称,它的位置,或者它可以省略(在Python 2.7及更高版本中),Python只会在数字顺序中使用!参数,当它们与替换字段匹配时

field_name可选地后跟一个转换字段,前面有一个感叹号:,还有一个format_spec(格式规范),前面有一个冒号b。这些指定替换值的非默认格式。此特定格式字符串没有转换字段,因此使用默认转换。

format_spec以字母结尾,指定我们想要的格式类型。我们的format_spec以b结尾,表示我们要将整数格式化为二进制。紧接在08b之前的数字指定了结果位串应该有多宽;通过在前面加零,我们说我们希望用零填充字符串而不是空格。因此,bit_len的format_spec将为我们提供8个字符宽的位字符串(如果需要,可以更宽地正确表示参数),短位字符串用零填充。

但我们需要更高级的东西,因为我们没有固定的位字符串宽度 - 我们希望我们的位字符串宽08b个字符。但那没关系,因为格式语法允许我们将替换字段放在另一个替换字段中!所以我们不是0{1}b而是'{val:0{width}b}'.format(val=i, width=bit_len) ,现在位置1中的参数值被用作位字符串宽度。

我之前提到过field_name可以是参数的名称,因此我们的原始格式表达式也可以写成:

'{:0{}b}'.format(i, bit_len)

我猜这种形式比原始形式更具可读性。 :)

我还提到可以省略field_name(在Python 2.7及更高版本中),如下所示:

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)]

这确实以不同的顺序产生子集,但是改变它并不难,我认为这个顺序更自然。

我们可以通过使用嵌套列表理解来使最后一个版本更紧凑:

def subsets(seq):
    z = [[]]
    for x in seq:
        z += [y + [x] for y in z]
    return z

FWIW,这是另一种制作子集的方法。它的效率有点低,但对于小输入序列来说却没问题。它以与先前版本相同的顺序生成子集。

max-width: certain-px-smaller-than-content;