5

New to Python, but I have been researching this for a couple hours. Forgive me if I missed something obvious.

I have a class called LineItem, which has an attribute _lineItems, a list of LineItems that belong to the given LineItem. A sub-list, basically.

I want to print out a LineItem and all of its sub-items (and the sub-items own sub-items), but I'm having trouble with the iteration.

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)

An example LineItem that's giving me the trouble is defined below as ext2, with three children.

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

I have this recursive function for iterating all the sub-items (and printing them numbered, like 1, 2, 2.1 as the first subitem of the second item, etc)

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

but when I try to use it

def main():
    print_line_item([ext1, ext2, ext3]) #ext1 has no children, prints fine
if __name__=="__main__":
    main()

I get

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

Okay, so somehow I'm screwing up lists.

if I add a couple print statements:

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

I get proof that a._lineItems is indeed a list, printed as follows:

[<__main__.LineItem object at 0x0227C430>, <__main__.LineItem object at 0x0227C5F0>, <__main__.LineItem object at 0x0227C670>]

and that the b I'm trying to pass to the recursing call is a memory address of a single LineItem

<__main__.LineItem object at 0x0227C430>

Sooo how am I actually supposed to do what I'm trying to do? I tried a couple things with .iter or ___iter___ but no luck.

Also, the if a._lineItems != [] doesn't seem to be working either (nor variations on that). I get printed lines of "None"

7
  • 1
    Don't use != [], use either if a._lineItems or if len(a._lintItems). Also, not to nit-pick, but this is not recursion. Commented Mar 20, 2014 at 1:39
  • 1
    How about you trim your Question down to a simple minimum reproducible example? Commented Mar 20, 2014 at 1:47
  • @sberry How is it not recursion? print_line_item goes through and calls itself with the sublists Commented Mar 20, 2014 at 1:51
  • An SSCCE would be a good idea. Also, you might try pylint - it's like an expert system about how to write good Python code. Commented Mar 20, 2014 at 1:53
  • (Short, Self Contained, Correct (Compilable), Example is what SSCCE stands for) Commented Mar 20, 2014 at 1:56

2 Answers 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

It might be correct version, not tested.

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
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Melug. This wasn't quite it but you got me much closer to a solution than anyone else.
0

It makes sense you're getting a not-iterable message--you're essentially recursing into print_line_item for each item in a list--and sooner or later, you'll hit something in a list that isn't iterable itself--and you just go on and call print_line_item() on it, which will try to iterate over it.

If you want to ask "is this item a list?" you could use isinstance(some-object, list). Or, if you want to allow for other iterable-but-not-list-things, you can use if isinstance(some-object, collections.Iterable) (you'll have to import collections).

2 Comments

Hi JoeL, I hear what you're saying, which is why the "if a._lineItems != []:" is in there - it's not working properly anyway, but in this example, I shouldn't have reached that base case yet anyway. I'm trying to iterate a list, which I proved was a list by printing it out right before trying to iterate. That's where I'm stuck (unless I'm still misunderstanding?)
"if a._lineItems != []" means "if a.lineItems is not the empty list", not "if it is not a list".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.