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
4 Answers 4
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]
8 Comments
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.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.x is iterable, and then special-casing when x has a known length of 1 (to avoid infinite recursion on strings).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
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.
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.