Python对象列表迭代“不可迭代”

5 投票
2 回答
23505 浏览
提问于 2025-04-17 23:04

我刚接触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”。

2 个回答

0

你收到“不可迭代”的提示是有道理的——你实际上是在对列表中的每个项目进行递归调用print_line_item。最终,你会遇到一个列表中不是可迭代的东西,然后你就会继续调用print_line_item(),这时它会尝试去迭代这个不可迭代的东西。

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

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

这可能是正确的版本,但还没有经过测试。

撰写回答