Why loop variable is not updated in python - python

This code is only printing 1 2 4 5..My question is that why p is not updated with the new array at 3rd iteration
p = [1, 2, [1, 2, 3], 4, 5]
for each in p:
if type(each) == int:
print each
else:
p = each
Actually to be precise when debugged the code I saw that it actually updating the value of p but each variable is not reinitialised again.

Because of if type(each) == int: line. Your third element is a list ([1, 2, 3]) and not an int, so it doesn't print anything.
Now what comes to changing the p variable: p is just a name for an object, not the object itself. If you do p = each inside the for loop, it doesn't affect the original object you're looping through, it just changes the name p to a local name, which points to a different object. As soon as that round of the loop ends, your for loop continues to do its business with the original object you were looping through.
So, notice that p = each doesn't change the existing object (the p you're looping through), it simply creates a new local name p which points to the value of each.
What you most likely want is something like this:
p = [1, 2, [1, 2, 3], 4, 5]
for each in p:
if isinstance(each, list):
for x in each:
print x
else:
print each
This then again, this isn't recursive, and you'd need a function for that:
def print_elements(iterable):
for element in iterable:
if isinstance(element, list):
print_elements(element)
else:
print element
If you want to unpack the values into one list to use them for something other than printing, you should use something like this:
def recursive_unpack(iterable):
for element in iterable:
if isinstance(element, list):
yield from recursive_unpack(element)
else:
yield element
Why I'm using isinstance() instead of type(): Differences between isinstance() and type() in python
Also, if you want this to apply to all iterables (my last example) and not just lists: In Python, how do I determine if an object is iterable?

the issue is in the else statement, you're basically saying that if the each is not an int (in your case it's a list) then set the list p to the inside list.
I think that what you're trying to do can be accomplished by something like
p = [1, 2, [1, 2, 3], 4, 5]
for element in p:
if type(element) == int:
print element
else:
for otherElement in element:
if type(otherElement) == int:
print otherElement
the else statement in this case goes through the inside list and checks the elements it contains (otherElement)

Related

printing items in a list represented by bit list

I have this problem on writing a python function which takes a bit list as input and prints the items represented by this bit list.
so the question is on Knapsack and it is a relatively simple and straightforward one as I'm new to the python language too.
so technically the items can be named in a list [1,2,3,4] which corresponds to Type 1, Type 2, Type 3 and etc but we won't be needing the "type". the problem is, i represented the solution in a bit list [0,1,1,1] where 0 means not taken and 1 means taken. in another words, item of type 1 is not taken but the rest are taken, as represented in the bit list i wrote.
now we are required to write a python function which takes the bit list as input and prints the item corresponding to it in which in this case i need the function to print out [2,3,4] leaving out the 1 since it is 0 by bit list. any help on this? it is a 2 mark question but i still couldn't figure it out.
def printItems(l):
for x in range(len(l)):
if x == 0:
return False
elif x == 1:
return l
i tried something like that but it is wrong. much appreciated for any help.
You can do this with the zip function that takes two tiers Lee and returns them in pairs:
for bit_item, item in zip(bit_list, item_list):
if bit_item:
print item
Or if you need a list rather than printing them, you can use a list comprehension:
[item for bit_item, item in zip(bit_list, item_list) if bit_item]
You can use itertools.compress for a quick solution:
>>> import itertools
>>> list(itertools.compress(itertools.count(1), [0, 1, 1, 1]))
[2, 3, 4]
The reason your solution doesn't work is because you are using return in your function, where you need to use print, and make sure you are iterating over your list correctly. In this case, enumerate simplifies things, but there are many similar approaches that would work:
>>> def print_items(l):
... for i,b in enumerate(l,1):
... if b:
... print(i)
...
>>> print_items([0,1,1,1])
2
3
4
>>>
You may do it using list comprehension with enumerate() as:
>>> my_list = [0, 1, 1, 1]
>>> taken_list = [i for i, item in enumerate(my_list, 1) if item]
>>> taken_list # by default start with 0 ^
[2, 3, 4]
Alternatively, in case you do not need any in-built function and want to create your own function, you may modify your code as:
def printItems(l):
new_list = []
for x in range(len(l)):
if l[x] == 1:
new_list.append(x+1) # "x+1" because index starts with `0` and you need position
return new_list
Sample run:
>>> printItems([0, 1, 1, 1])
[2, 3, 4]

Adding a duplicate item on to a list

I'm trying to add the last item of a list onto the list my code is:
def dup_last(data):
data.append([-1])
return data
and calling for the function is:
item = dup_last([1,2,3])
print(item)
but I want my output to be within only one set of brackets like:
[1, 2, 3, 3]
data.append([-1])
Here you are appending [-1], a list with an element of -1, change it to:
data.append(data[-1])
In addition to other answers, I would also suggest to use slicing notation [:] when dealing with lists to prevent getting list index out of range errors in case there is no item:
def dup_last(data):
data.append(data[-1])
return data
The above function will raise IndexError if data is empty list:
>>> print dup_last([])
----> 2 data.append(data[-1])
3 return data
4
IndexError: list index out of range
When you update your function as follows, you no longer get that kind of error:
def dup_last(data):
data.extend(data[-1:])
return data
>>> print dup_last([])
[]
>>> print dup_last([1])
[1, 1]
>>> print dup_last([1, 2])
[1, 2, 2]
There is a good explanation in this SO question about how slicing works in Python.
You need to do data.append(data[-1]); data.append([-1]) appends a value which is a list containing only -1, so your result will be [1, 2, 3, [-1]].
Note that this will modify the list in-place, so whichever list you pass in will also have the last element duplicated, not just the list you get out (though they could be the same list).
I wouldn't use a function for this; just do data.append(data[-1]) instead of data = dup_last(data), or even dup_last(data). Also, it's probably better to just add the duplicate manually if you're working with a list literal; data = [1, 2, 3, 3] vs data = dup_last([1, 2, 3]) or similar.

Finding lowest value within a nested list?

Im trying to write a function that takes a list and can print the lowest integer that is within that list. Now i'm trying to figure out what to do where this works with nested lists that if the lowest number is within one of those nested lists then overall it will print that number. My code is here:
def listMin():
list2 = [3,4,[2,99,8],7]
for i in range (len(list2)):
if type(list2[i]) == type([]):
y=min(i)
list2.append(y)
print "hello"
if len(list2)== 0:
return None
else:
x= min(list2)
print x
listMin()
while this seems like it should print the number 2 it doesnt and just gives me an error once it reaches the nested list saying:
TypeError: 'int' object is not iterable
ive tried multiple things but i'm having a hard time as to why this sort of thing isn't working.
Nesting One Deep
In your example, the list is nested only one deep. If this is the case in general, then try:
>>> list2 = [3,4,[2,99,8],7]
>>> min(x if isinstance(x, int) else min(x) for x in list2)
2
Nesting of Arbitrary Depth
If deeper nesting is allowed, define this recursive function:
>>> def rmin(lst): return min(x if isinstance(x, int) else rmin(x) for x in lst)
...
In operation:
>>> rmin(list2)
2
Or, with deeper nesting:
>>> list3 = [3,4,[[2,99],8],7]
>>> rmin(list3)
2
>>> list4 = [3, 4, [[2, [99, 1]], 8], 7]
>>> rmin(list4)
1
How it works
The function rmin consists of the single line:
return min(x if isinstance(x, int) else rmin(x) for x in lst)
As you can see, this is a list comprehension that looks at every value x of the list lst.
Let's divide the argument of min into two parts. The first is:
x if isinstance(x, int) else rmin(x)
This returns x if x is an integer. Otherwise, it calls rmin on x. In the latter case, rmin recursively looks at every value in x and returns the minimum.
The second part of the argument of min is:
for x in lst
This is just the usual for a list comprehension. It extracts each value in lst in turn and assigns it to x.
In general, you could flatten your list of lists and search for min in the flattened list. There are many recipes for flattening. Here is one that I took from here.
import collections
def flatten(iterable):
for el in iterable:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
yield from flatten(el)
else:
yield el
list2 = [3,4,[2,99,8],7]
print(list(flatten(list2)))
# [3, 4, 2, 99, 8, 7]
print(min(flatten(list2)))
# 2
This will work on multiple nested list as well, e.g.:
list2 = [3,4,[2,99,8,[-1,-2]],7]
print(list(flatten(list2)))
# [3, 4, 2, 99, 8, -1, -2, 7]
print(min(flatten(list2)))
# -2
The problem is caused by the line
y=min(i)
where i is an integer but not a list. You probably want y = min(list2[i]).
Note that while you have appended this y back to the original list, the loop would not reach the newly added element, since i will only range up to the original length of the list.
With some simple Python idioms, your original idea can be expressed in a more readable way as follows:
def listMin():
lst = [3,4,[2,99,8],7]
for x in lst:
if type(x) == list:
lst.append(min(x))
print min(lst)
listMin()
When I needed to do something similar, I wrote following:
import copy
def nestedMin(list_):
target_list = copy.deepcopy(list_) # to keep original list unchanged
for index in range (len(target_list)):
if type (target_list[index]) is list:
target_list[index] = nestedMin(target_list[index])
return min(target_list)
I know that it is not very efficient, keeps doing deepcopy; but it's readable and it does the job :)
Example:
list1 = [2,3,[4, -5, [7, -20]]]
print nestedMin(list1) # prints -20
print list1 # prints [2, 3, [4, -5, [7, -20]]]

Reversing Order of List in Python

I have been given the following piece of code:
def two_pair(ranks):
"""If there are two pair, return the two ranks as a
tuple: (highest, lowest); otherwise return None."""
pair = kind(2,ranks)
lowpair = kind(2, list(reversed(ranks)))
if pair and lowpair != pair:
return (pair,lowpair)
else:
return None
In the lowpair variable, why does list() need to be stated? Why can't you just say reversed(ranks). ranks is a list. Is it not already implied?
reversed returns an iterator, not a list. We need to explicitly convert that to a list, unless we just want to iterate it.
a = [1, 2, 3]
print reversed(a) # <listreverseiterator object at 0x7fc57d746790>
That is why we have to use list to get the actual reversed list, like this
print list(reversed(a)) # [3, 2, 1]
If you want shorter code you could do ranks[::-1] instead of list(reversed(ranks)).
>>> ranks = [1,2,3]
>>> ranks[::-1]
[3, 2, 1]
reversed(ranks) isn't a reversed list. It's an iterator:
>>> reversed([1, 2, 3])
<listreverseiterator object at 0x0000000001DDE9E8>
The list call is necessary to get a reversed list.

Deleting items in a list in python

Tried deleting items in a list, no success.
>>> r = [1,2,3,4,5]
>>> for i in r:
if i<3:
del i
>>> print r
[1, 2, 3, 4, 5]
I even tried filtering it,
>>> def f(i):
True if i>2 else False
>>> print list(filter(f,r))
[]
I do not understand why the first one is not working. And I dont understand the result at all, when I use filter(function,iterable).
EDIT:
Seeing Paulo's comment below, now I do not understand why this works.
>>> for i in r:
if i<3:
r.remove(i)
>>> print r
[3, 4, 5]
Shouldn't the iterator problem be still there, and shouldn't the code end up removing only the first element (r[0])
Use a list comprehension instead:
[i for i in r if i >= 3]
and retain instead of delete.
Your filter never returned the test; so you always return None instead and that's false in a boolean context. The following works just fine:
def f(i):
return i > 2
Your initial attempt failed because del i unbinds i, but the list remains unaffected. Only the local name i is cleared.
If you want to delete an item from a list, you need to delete the index:
del r[0]
deletes the first element from the list.
Even if you did manage to delete indices the loop would have held some suprises:
>>> for i, element in enumerate(r):
... if element < 3:
... del r[i]
...
>>> r
[2, 3, 4, 5]
This version fails because the list iterator used by the for loop doesn't know you deleted elements from the list; deleting the value at index 0 shifts up the rest of the list, but the loop iterator looks at item 1 regardless:
first iteration, r = [1, 2, 3, 4, 5], iterator index 0 -> element = 1
second iteration, r = [2, 3, 4, 5], iterator index 1 -> element = 3
I do not understand why the first one is not working.
It is not working because the statement del i undefines the variable i - that is, it deletes it from the scope (global or local) which contains it.
And I dont understand the result at all, when I use filter(function,iterable)
Your function, f does not contain a return statement. Accordingly, it always returns None, which has the boolean equivalent value of False. Thus, filter excludes all values.
What you should probably be doing is filtering using a comprehension, and replacing the list, like so:
r = [i for i in r if i >= 3]
Or, if you really do want to delete part of the original list and modify it, use del on a slice of the list:
del r[:3]
Seeing Paulo's comment below, now I do not understand why [using remove] works.
Because remove(r) searches for the value r in the list, and deletes the first instance of it. Accordingly, repeated modification of the list does not affect the iteration that happens inside remove. However, note that it is still susceptible to the same error, if removal of an item leads to an item being skipped in iteration of the list.

Categories