2

Basically, I'm trying to flatten a list in my function but ignore that (you can also ignore the print functions I put in). take x = [[1,2,3],4,5] to be my variable. I call prob7(x) but the issue is that when type([1,2,3]) gets checked == list, it returns false. Why is that? I explicitly check this on the interpreter command line and it returns true. But inside the function, I get a false.

Just a bug that I missed because I'm sleepy or am I misunderstanding some part of the Python language? I run version 2.6 if it matters.

def prob7(list): # flatten a list
    tempList = []
    if list: # meaning if there are elements in the list and it is not empty
        for i in list:
            if type(i) != list:
                print tempList,'if',i,type(i)==list
                tempList.append(i)
            else:
                print tempList,'else',i
                tempList.extend(prob7(i))

    return tempList
2
  • Watch out for lists nested more than sys.getrecursionlimit()(about 1000 lists) deep. Since you're calling the function on the nested lists, you can hit the recursion depth limit and get a RecursionError. Commented Aug 31, 2011 at 6:52
  • dang, thank you all. much more information than i needed but still very useful overall Commented Aug 31, 2011 at 16:25

4 Answers 4

3

Just not use 'list' as a variable name and use isinstance(var, list) instead of type(var) == list. Please find corrected sample below.

def prob7(mylist): # flatten a list
    tempList = []
    if mylist: # meaning if there are elements in the list and it is not empty
        for i in mylist:
            if not isinstance(i, list):
                print tempList, 'if', i, isinstance(i, list)
                tempList.append(i)
            else:
                print tempList, 'else', i
                tempList.extend(prob7(i))    
    return tempList

Or if you don't really required to use recursion and you don't care about values order then you can use something like this:

lVals = [[1,2,3],4,5, [1,[4,7]]]

def make_flat(mylist): # flatten a list    
    while any(isinstance(x, list) for x in mylist):
        for i, val in enumerate(mylist):
            if isinstance(val, list):                
                mylist.extend(mylist.pop(i))        
                break
    return mylist

make_flat(lVals)
>>> [4, 5, 1, 2, 3, 1, 4, 7]
Sign up to request clarification or add additional context in comments.

8 Comments

Checking for an empty list before iterating over it is useless: iterating over an empty list will correctly do nothing. Special cases aren't special enough.
@Karl - sure - i just tried to fix OP sample, my code is second one.
works if i use type( ) ==, why use isinstance? i thought you only need that for subclasses
@david - sure it works but it is more pythonic to use isinstance for this purpose.
Your second example mutates the list it is iterating over. That can lead to unexpected results.
|
3

Artisom has your answer. In addtion, type checks are not very Pythonic. Duck typing often is the way to go. In case your elements are numbers only, the following does the job too, without explicit type checks but behavior checks:

def prob7(inlist): # flatten a list
    outlist = []
    for x in inlist:
        try:
            outlist += x
        except TypeError:
            outlist.append(x)
    return outlist

Note that string elements in this implementation would behave like nested lists. Anyway, just wanted to illustrate what it means to expect behavior, not types.

3 Comments

+= for lists is a bit hacked. The simplest way to make strings "behave" here would be to just use outlist = outlist + x. Yes, with the list type in particular, this is not equivalent. Anyway, this only flattens one level.
Indeed, outlist.extend(...) might be better here. And yes, outline + x works as expected with strings but would also raise TypeErrors for tuple elements and it would always create new list objects. I've chosen the one level solution to mainly illustrate the duck typing idea. For a truly recursive flattening, instanceof checks would be more explicit in my opinion.
If you want to recursively flatten everything that can be flattened, I would duck-type by checking for whether x is iterable, and then special-casing when x has a known length of 1 (to avoid infinite recursion on strings).
0

Some alternate approaches:

# Iterative, but more functional-style
def flatten(a_list):
  while any(isinstance(x, list) for x in a_list):
    a_list = sum((x if isinstance(x, list) else [x] for x in a_list), [])
  return a_list

# Using a generator recursively, 
# then evaluating the generator to produce the list
# instead of explicitly appending each element.
def flatten_gen(a_list):
  for x in a_list:
    if isinstance(x, list):
      for y in flatten_gen(x): yield y
    else: yield x

def flatten(a_list): return list(flatten_gen(a_list))

Comments

0

The problem here is you are using a local variable name (list) that is the same as the global list type. You should change your variable name. Also, when checking types like that you can use the is operator.

type(l) is list

But here's my version of flatten.

def flatten(alist):
    rv = []
    for val in alist:
        if isinstance(val, list):
            rv.extend(flatten(val))
        else:
            rv.append(val)
    return rv

This does not alter the original list, but returns a new list. This is consistent with most other patterns.

1 Comment

thank you, clearest answer here, though i will check out the isinstance stuff.

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.