Python迭代对象列表时报错"not iterable"

5

我刚接触Python,但已经研究了几个小时。如果我漏掉了一些显而易见的东西,请原谅。

我有一个名为LineItem的类,它有一个属性 _lineItems,是属于给定 LineItem 的 LineItem 的列表。基本上是一个子列表。

我想打印出一个 LineItem 及其所有子项(以及子项自己的子项),但我在迭代方面遇到了问题。

from decimal import * 
class LineItem(object):
    """
    Instance attributes:
      amount: Decimal
      _lineItems: list of child internal lineitems (possibly an empty list)
      isInternal: bool
    """

    def __init__(self, **kw):
        self.amount = Decimal(0)
        self._lineItems = []
        self.isInternal = False
        for k, v in kw.items():
            setattr(self, k, v)

一个给我带来麻烦的LineItem示例被定义为ext2,它有三个子元素。
# External line item with one level of children
int1 = LineItem(amount=Decimal('1886.75'), description='State Dues',
         isInternal=True)
int2 = LineItem(amount=Decimal('232.50'), description='National Dues',
         isInternal=True)
int3 = LineItem(amount=Decimal('50'), description='Processing Fee',
         isInternal=True)
ext2 = LineItem(amount=Decimal('2169.25'), description='Dues',
         _lineItems=[int1, int2, int3])

我有一个递归函数,用于迭代所有子项(并将它们编号打印出来,例如1、2、2.1作为第二个项目的第一个子项目等等)。

def print_line_item(LineItems):
    count = 1
    for a in LineItems:
        print count, ' ', a.description, ' (', a.amount, ')'
        if a._lineItems != []:
            for b in a._lineItems:
                print count, '.', print_line_item(b),
        count+=1

但是当我尝试使用它时
def main():
    print_line_item([ext1, ext2, ext3]) #ext1 has no children, prints fine
if __name__=="__main__":
    main()

我理解了

line 56, in print_line_item
    print count, '.', print_line_item(b),
line 51, in print_line_item
    for a in LineItems:
TypeError: 'LineItem' object is not iterable

好的,所以我在列表方面出了点问题。

如果我添加几个打印语句:

def print_line_item(LineItems):
    count = 1
    for a in LineItems:
        print count, ' ', a.description, ' (', a.amount, ')'
        if a._lineItems != []:
            print a._lineItems
            for b in a._lineItems:
                print b
                print count, '.', print_line_item(b),
        count+=1

我证实 a._lineItems 确实是一个列表,以下为打印结果:
[<__main__.LineItem object at 0x0227C430>, <__main__.LineItem object at 0x0227C5F0>, <__main__.LineItem object at 0x0227C670>]

我试图传递给递归调用的b是单个LineItem的内存地址。

<__main__.LineItem object at 0x0227C430>

我该如何实现我所要做的事情?我尝试了一些 .iter 或 ___iter___ 的方法,但都没有成功。

另外,if a._lineItems != [] 似乎也无效(或其变体)。我得到打印的行是“None”。


1
不要使用!= [],而应该使用if a._lineItems或者if len(a._lintItems)。另外,不想挑刺,但这并不是递归。 - sberry
1
你能否将你的问题简化为一个最小可重现的例子? - Russia Must Remove Putin
@sberry 这不是递归吗?print_line_item 通过调用其子列表来调用自身。 - Foon
SSCCE代表的是“短小、自包含、可编译、正确”的编程内容示例。 - Foon
@sberry 我实际上已经尝试了这两种方法,但仍然得到了“None”的输出。 - VMills
显示剩余2条评论
2个回答

3
def print_line_item(LineItems):
    count = 1
    for a in LineItems:
        print count, ' ', a.description, ' (', a.amount, ')'
        if a._lineItems != []:
            for b in a._lineItems:
                print count, '.', print_line_item(b),
        count+=1

这可能是正确的版本,但没有经过测试。
def print_line_item(LineItems, precedingNumber='1'):
    count = 1
    for a in LineItems:
        print precedingNumber, '.', count, ' ', a.description, ' (', a.amount, ')'
        print_line_item(a._lineItems, precedingNumber + '.' + count),
        count+=1

谢谢Melug。虽然这不是完全正确的解决方案,但你让我比其他人更接近解决问题了。 - VMills

0

你收到了一个不可迭代的消息,这是有道理的——你实际上正在为列表中的每个项目递归进入print_line_item函数——而 sooner or later,你会遇到一个列表中不可迭代的东西——然后你继续调用print_line_item(),它将尝试对其进行迭代。

如果你想问“这个项目是一个列表吗?”你可以使用isinstance(some-object, list)。或者,如果你想允许其他可迭代但不是列表的东西,你可以使用if isinstance(some-object, collections.Iterable)(你需要导入collections)。


嗨JoeL,我明白你的意思,这就是为什么“if a._lineItems!= []:”在那里的原因 - 它无论如何都不起作用,但在这个例子中,我本来不应该达到那个基本情况。我正在尝试迭代一个列表,我通过在尝试迭代之前打印出它来证明它是一个列表。这就是我卡住的地方(除非我还有误解?) - VMills
"if a._lineItems != []" 的意思是“如果a.lineItems不是空列表”,而不是“如果它不是一个列表”。 - Joel Burton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接