Python TypeError 遍历列表

Posted

技术标签:

【中文标题】Python TypeError 遍历列表【英文标题】:Python TypeError in traversing a list 【发布时间】:2012-06-08 16:40:34 【问题描述】:

我正在自学 Python 3.2,并且正在尝试制作一个程序来匹配名称列表。 pList 是一个多维列表,第 0 列有一个字符串,第 1 列有一个整数,第 2 列有一个布尔值。但是,每当我尝试调用此函数时(仅当列表中的行数为偶数时才会运行),我得到一个类型错误。

Traceback (most recent call last):
 File "C:\Users\METC\Dropbox\assassins.py", line 150, in <module>
main()
 File "C:\Users\METC\Dropbox\assassins.py", line 11, in main
update(ops, pList)
 File "C:\Users\METC\Dropbox\assassins.py", line 125, in update
b = match(pList)
 File "C:\Users\METC\Dropbox\assassins.py", line 47, in match
q, p = 0
TypeError: 'int' object is not iterable

任何帮助都将不胜感激,但请记住,我是该语言的初学者,所以要温柔。 :) 我不介意你是否太技术化;我有计算机科学方面的经验。

def match(pList):
    b = []
    z = len(pList)-1
    for x in range(z):
        b.append([pList[x][0],0])
    for x in range(z):
        isValid = False
        q, p = 0
        while isValid == False:
            q = random.randint(0, z)
            print('q is ' + str(q))
            if q > z:
                isValid = False
            elif q < 0:
                isValid = False
            elif pList[q][1] == True:
                isValid = False
            else:
                isValid = True
        isMatch = False
        while isMatch == False:
            if pList[q][1] == False:
                isValid = False
                while isValid == False:
                    p = random.randint(0,z)
                    print('p is ' + str(p))
                    if p > z:
                        isValid = False
                    elif p < 0:
                        isValid = False
                    elif pList[p][2] == True:
                        isValid = False
                    else:
                        if q == p:
                            isValid = False
                        else:
                            isValid = True
                print('match valid')
                b[q][1] = pList[p][0]
                isMatch = True
    print('')
    return b

【问题讨论】:

错误是哪一行,您能否发布完整的回溯并在代码中用注释标记该行?此外,考虑如何将代码分解为更小的函数也不是一个坏主意。 也不妨借此机会学习一下如何使用内置调试器。 docs.python.org/library/pdb.html 您可以将import pdb; pdb.set_trace() 放入您的代码中,在这种情况下,当执行到它时,它将“冻结”并允许您检查变量或逐行执行。 【参考方案1】:

你把逻辑远远地弄得太复杂了,以至于我必须进行几次传递才能缩小规模并向你展示你在做什么错了。

首先,正如其他人指出的那样,我们将修复实际报告的错误。同时,我们将应用一个简单的原则:不要与布尔文字比较。你不会说“如果真的在下雨,我需要一把伞”。你说“如果下雨,我需要一把伞”。所以去掉多余的东西。 if isValidif isValid == True清晰,因为它准确地突出了 isValid 的含义。我还将取出调试跟踪(print 语句,这些语句显然只是为了检查代码是否在做正确的事情;先简化代码,然后要检查的就更少了)。

def match(pList):
    b = []
    z = len(pList)-1
    for x in range(z):
        b.append([pList[x][0],0])
    for x in range(z):
        isValid = False
        q = p = 0
        while not isValid:
            q = random.randint(0, z)
            if q > z:
                isValid = False
            elif q < 0:
                isValid = False
            elif pList[q][1]:
                isValid = False
            else:
                isValid = True
        isMatch = False
        while not isMatch:
            if not pList[q][1]:
                isValid = False
                while not isValid:
                    p = random.randint(0,z)
                    if p > z:
                        isValid = False
                    elif p < 0:
                        isValid = False
                    elif pList[p][2]:
                        isValid = False
                    else:
                        if q == p:
                            isValid = False
                        else:
                            isValid = True
                b[q][1] = pList[p][0]
                isMatch = True
    return b

接下来,我们将简化条件逻辑。首先,从 random.randint(0, z) 返回的结果 不能&lt; 0&gt; z,无论如何。这是功能的一部分。因此,编写代码来处理这些情况是没有意义的,实际上这样做是错误的。编写代码来处理某些事情意味着它实际上可能会发生。这对阅读代码的人来说是一种分心,而且是一个谎言。它在重要的事情(对random.randint 的调用和对pList 值的检查)之间放置了额外的空间。

我们还将简化 if/else 对,只需相应地设置另一个布尔值。出于同样的原因,你不会写

if x == 1:
    y == 1
elif x == 2:
    y == 2
# ... etc. ad infinitum for every possible integer value of x

你也不应该对布尔值做同样的事情。最后,我们可以而且应该使用逻辑 andor 来连接布尔条件。

def match(pList):
    b = []
    z = len(pList)-1
    for x in range(z):
        b.append([pList[x][0],0])
    for x in range(z):
        isValid = False
        q = p = 0
        while not isValid:
            q = random.randint(0, z)
            isValid = not pList[q][1]
        isMatch = False
        while not isMatch:
            if not pList[q][1]:
                isValid = False
                while not isValid:
                    p = random.randint(0,z)
                    isValid = not pList[p][2] and (q != p)
                b[q][1] = pList[p][0]
                isMatch = True
    return b

我的下一步将是修复列表索引。索引到列表通常不是你真正想要的,实际上它在这里引入了一个错误。很明显,您想要遍历pList 的每一“行”;但是range(z) 为您提供了从0z-1 的数字,因此在计算z 时从len(pList) 中减去1 是不正确的。例如,如果 pList 有 5 个元素,您将计算 z = 4,并生成 range(z) = [0, 1, 2, 3]。你永远不会访问pList[4]b 将只有 4 个元素。

从根本上说,您对z 做了两件事。一个是使循环运行的次数与pList 中的“行”一样多,并且(在第一个循环中)对每个“行”执行一些操作。为此

这很重要:range 不是魔法,它与 for 循环没有特殊联系。它只是一个产生数字列表的函数。在 Python 中,for 循环直接为您提供元素。所有这些索引废话就是 - 废话,最好留给能力较弱的语言。如果你想对列表中的每个元素做一些事情,那么对列表中的每个元素做一些事情,方法是编写循环遍历列表中每个元素的代码。直接地。不要超过一些单独的索引列表,然后您可以使用这些索引将其索引回原始索引。这会让事情变得复杂。

您对z 所做的第二件事是生成一个可能的索引的随机数,以便您可以索引到pList 以获得随机行。换句话说,您只想选择一个随机行。所以只需选择一个随机行random 模块直接提供此功能:该函数称为random.choice,它的功能与听起来完全一样。

这里有一个小问题:原始代码比较 p == q,即比较两个随机选择的列表索引是否相等。如果我们不再编制索引,那么我们就没有可比较的索引。为了解决这个问题,我们需要了解最初的目的是什么:再次检查新选择的行是否实际上是旧选择的行。同样,我们通过直接检查来简化:我们选择新行而不是新索引,然后查看它是否 is 旧行。

我们还有一个问题,我们需要从b 中选择一个对应的行,该行对应于pList 中我们之前识别为q 的行。为了解决这个问题,我们可以同时选择b 行和pList 行。这有点棘手:我的方法是列出成对的行 - 在每一对中,将有来自 b 的一行,然后是来自 pList 的一行。这不需要任何复杂的东西——实际上有一个内置函数可以将bpList 完全按照我们想要的方式拼接在一起:它被称为zip。无论如何,从该行对列表中选择了一个行对后,我们只需将两行解压缩为两个变量 - 使用您最初错误使用的 q, p = ... 语法,事实证明。这就是它的用途。

通过这些更改,我们实际上可以完全摆脱pqz。这很好,因为根本不清楚这些名字应该是什么意思。

def match(pList):
    b = []
    for row in pList:
        b.append([row[0], 0])
    for row in pList:
        isValid = False
        while not isValid:
            first_row, b_row = random.choice(zip(pList, b))
            isValid = not first_row[1]
        isMatch = False
        while not isMatch:
            if not first_row[1]:
                isValid = False
                while not isValid:
                    second_row = random.choice(pList)
                    isValid = not second_row[2] and (first_row is not second_row)
                b_row[1] = second_row[0]
                isMatch = True
    return b

是时候进行一些更合乎逻辑的清理了。在第一个 while 循环中,我们将继续循环,直到 isValid 变为 true。也就是说,直到not first_row[1] 变为真。在第二个 while 循环中,first_row 永远不会改变,因此,由于循环开始时not first_row[1] 为真,因此它将始终保持为真。因此,完全没有必要进行 if-check。

一旦结束,我们发现第二个while循环实际上也完全没用:它将循环while not isMatch,即直到isMatchisMatch 是什么?好吧,在我们开始循环之前,它是False,在循环结束时,它是True。总是。所以我们知道这段代码只会运行一次。我们进入循环,走到最后,将isMatch 设置为true,然后退出,因为刚刚设置为true 的isMatch 为true。只运行一次的代码不需要循环;这只是代码。

我在这里要做的另一件事是清理while isValid 循环,在我们完成后将它们转换为breakbreak 不是邪恶的continue 也不是)。它们实际上简化了我们对布尔值的思考,因为我们不再检查not isValid(强调not);我们只是直接与我们分配给isValid 的内容进行比较。这意味着我们也摆脱了isValid 变量,这又是一个实际上并没有告诉我们太多信息的名称

def match(pList):
    b = []
    for row in pList:
        b.append([row[0], 0])
    for row in pList:
        while True:
            first_row, b_row = random.choice(zip(pList, b))
            if not first_row[1]:
                break
        while True:
            second_row = random.choice(pList)
            if not second_row[2] and (first_row is not second_row):
                break
        b_row[1] = second_row[0]
    return b

最后一件事:我们可以更干净地构造b。通过附加元素来建立一个列表是一个傻瓜游戏。 不要告诉 Python 如何构建列表。它知道怎么做。相反,要求一个符合您的规范的列表,并带有列表理解。这比解释更简单(如果你需要解释,请咨询谷歌),所以我继续给你一个版本:

def match(pList):
    b = [[row[0], 0] for row in pList]
    for row in pList:
        while True:
            first_row, b_row = random.choice(zip(pList, b))
            if not first_row[1]:
                break
        while True:
            second_row = random.choice(pList)
            if not second_row[2] and (first_row is not second_row):
                break
        b_row[1] = second_row[0]
    return b

从现在开始,如果不了解您实际上在做什么,就很难纠正或改进任何事情。经过这么多工作,我还是不知道!

按照您的设置方式,您可以选择随机行,次数与行数一样多 - 但您仍然可以选择重复行。那是你真正想要的吗?还是您想以随机顺序选择每一行一次?无论如何,以随机顺序执行它有什么意义?

选择第一行后,您随机选择第二行进行匹配。你真的想要每第一行一个随机行吗?还是您想尝试所有可能的行对?

这些数据到底是什么?输出b 数据代表什么? pList 到底是什么,为什么叫pList?你用这个match 函数“匹配”了什么?老实说,我无法想象。

【讨论】:

谢谢。它应该返回匹配名称的列表,其中 pList 中输入的名称按照 API 的要求随机配对。每个名字都需要一对不同的对。我想在我发布之前我应该​​更好地评论它。 +1 虽然它不应该是公认的答案。代码审查仍然很棒【参考方案2】:

这是你的构造:

q, p = 0

它试图通过迭代来解压单个 int。这不是有效的 Python 语法。我想错误可能会更好。

用途:

q = p = 0

改为。

【讨论】:

+1,但是(nitpick)这在语法上是有效的 Python;错误是语义错误。 我刚刚更改了它,并在第二个 while 循环的倒数第二行中收到了一个关于列表索引超出范围的新错误,这应该不是问题,因为我保留了随机整数在列表范围内。

以上是关于Python TypeError 遍历列表的主要内容,如果未能解决你的问题,请参考以下文章

TypeError: unhashable type: 'list' 当我想将一些属性值包含到列表中时

TypeError:字符串索引必须是整数,而不是 Python 字典中的 str

python: "TypeError: 'type' object is not subscriptable"

2. python提示:TypeError: unhashable type: 'list'

Python "TypeError: unhashable type: 'slice'" 用于编码分类数据

Python报错:TypeError: data type not understood