Explanation for the functionality of tuples in for loops - python

if a tuple is iterable why the following code results to an error?
I have read many threads about this but I am not able to understand.
tp1 = (5,7)
for i,j in tp1:
print(i,j)

When you use the for i,j in tp1: syntax, Python is expecting that item is an iterable of iterables, which can be unpacked into the variables i and j. In this case, when you iterate over item, the individual members of item are int's, which cannot be unpacked
if you want to unpack the items in the tuple, you should do it like this:
for i in tp1:
print(i)
and if you want to iterate throught that way you need tuple or list inside tuple or list
(i.e any iterable inside iterable)
a = [(1,2), (3,4)]
for i, j in a: # This unpacks the tuple's contents into i and j as you're iterating over a
print(i, j)
hope it would help you

If a tuple is iterable why the following code results to an error?
Because for i, j in (5, 7, ..., n) is essentially equivalent to:
i, j = 5
i, j = 7
...
i, j = n
And the resulting int on the right-hand side is not iterable. You cannot "unpack"* each element of the tuple further because it is a single integer.
What you can do is the more straightforward assignment:
tp1 = (5, 7)
i, j = tp1
The syntax that you're currently using would apply if each element of the tuple is iterable, such as :
>>> tp2 = (['a', 5], ['b', 6])
>>> for i, j in tp2: print('%s -> %d' % (i, j))
...
a -> 5
b -> 6
*From comments: Can you explain to me what you mean with the phrase 'you cannot "unpack" each element of the tuple further because it is a single integer'?
Unpacking is the concept of expanding or extracting multiple parts of a thing on the right hand side into multiple things on the left-hand side.
Unpacking, extended unpacking, and nested extended unpacking gives a thorough overview of unpacking. There are also some extended forms of unpacking syntax specified in PEP 3132.

Your for loop is trying to unpack a tuple from each element of the tuple. You could instead do:
for i in tp1:
print i
or you could just do:
i, j = tpl
print(i, j)
but combining the two only makes sense if you have a tuple of tuples, like ((5, 7), (9, 11)).

Related

Using multiple variables in a for loop in Python

I am trying to get a deeper understanding to how for loops for different data types in Python. The simplest way of using a for loop an iterating over an array is as
for i in range(len(array)):
do_something(array[i])
I also know that I can
for i in array:
do_something(i)
What I would like to know is what this does
for i, j in range(len(array)):
# What is i and j here?
or
for i, j in array:
# What is i and j in this case?
And what happens if I try using this same idea with dictionaries or tuples?
The simplest and best way is the second one, not the first one!
for i in array:
do_something(i)
Never do this, it's needlessly complicating the code:
for i in range(len(array)):
do_something(array[i])
If you need the index in the array for some reason (usually you don't), then do this instead:
for i, element in enumerate(array):
print("working with index", i)
do_something(element)
This is just an error, you will get TypeError: 'int' object is not iterable when trying to unpack one integer into two names:
for i, j in range(len(array)):
# What is i and j here?
This one might work, assumes the array is "two-dimensional":
for i, j in array:
# What is i and j in this case?
An example of a two-dimensional array would be a list of pairs:
>>> for i, j in [(0, 1), ('a', 'b')]:
... print('i:', i, 'j:', j)
...
i: 0 j: 1
i: a j: b
Note: ['these', 'structures'] are called lists in Python, not arrays.
Your third loop will not work as it will throw a TypeError for an int not being iterable. This is because you are trying to "unpack" the int that is the array's index into i, and j which is not possible. An example of unpacking is like so:
tup = (1,2)
a,b = tup
where you assign a to be the first value in the tuple and b to be the second. This is also useful when you may have a function return a tuple of values and you want to unpack them immediately when calling the function. Like,
train_X, train_Y, validate_X, validate_Y = make_data(data)
More common loop cases that I believe you are referring to is how to iterate over an arrays items and it's index.
for i, e in enumerate(array):
...
and
for k,v in d.items():
...
when iterating over the items in a dictionary. Furthermore, if you have two lists, l1 and l2 you can iterate over both of the contents like so
for e1, e2 in zip(l1,l2):
...
Note that this will truncate the longer list in the case of unequal lengths while iterating. Or say that you have a lists of lists where the outer lists are of length m and the inner of length n and you would rather iterate over the elements in the inner lits grouped together by index. This is effectively iterating over the transpose of the matrix, you can use zip to perform this operation as well.
for inner_joined in zip(*matrix): # will run m times
# len(inner_joined) == m
...
Python's for loop is an iterator-based loop (that's why bruno desthuilliers says that it "works for all iterables (lists, tuples, sets, dicts, iterators, generators etc)". A string is also another common type of iterable).
Let's say you have a list of tuples. Using that nomenclature you shared, one can iterate through both the keys and values simultaneously. For instance:
tuple_list = [(1, "Countries, Cities and Villages"),(2,"Animals"),(3, "Objects")]
for k, v in tuple_list:
print(k, v)
will give you the output:
1 Countries, Cities and Villages
2 Animals
3 Objects
If you use a dictionary, you'll also gonna be able to do this. The difference here is the need for .items()
dictionary = {1: "Countries, Cities and Villages", 2: "Animals", 3: "Objects"}
for k, v in dictionary.items():
print(k, v)
The difference between dictionary and dictionary.items() is the following
dictionary: {1: 'Countries, Cities and Villages', 2: 'Animals', 3: 'Objects'}
dictionary.items(): dict_items([(1, 'Countries, Cities and Villages'), (2, 'Animals'), (3, 'Objects')])
Using dictionary.items() we'll get a view object containig the key-value pairs of the dictionary, as tuples in a list. In other words, with dictionary.items() you'll also get a list of tuples. If you don't use it, you'll get
TypeError: cannot unpack non-iterable int object
If you want to get the same output using a simple list, you'll have to use something like enumerate()
list = ["Countries, Cities and Villages","Animals", "Objects"]
for k, v in enumerate(list, 1): # 1 means that I want to start from 1 instead of 0
print(k, v)
If you don't, you'll get
ValueError: too many values to unpack (expected 2)
So, this naturally raises the question... do I need always a list of tuples? No. Using enumerate() we'll get an enumerate object.
Actually, "the simplest way of using a for loop an iterating over an array" (the Python type is named "list" BTW) is the second one, ie
for item in somelist:
do_something_with(item)
which FWIW works for all iterables (lists, tuples, sets, dicts, iterators, generators etc).
The range-based C-style version is considered highly unpythonic, and will only work with lists or list-like iterables.
What I would like to know is what this does
for i, j in range(len(array)):
# What is i and j here?
Well, you could just test it by yourself... But the result is obvious: it will raise a TypeError because unpacking only works on iterables and ints are not iterable.
or
for i, j in array:
# What is i and j in this case?
Depends on what is array and what it yields when iterating over it. If it's a list of 2-tuples or an iterator yielding 2-tuples, i and j will be the elements of the current iteration item, ie:
array = [(letter, ord(letter)) for letter in "abcdef"]
for letter, letter_ord in array:
print("{} : {}".format(letter, letter_ord))
Else, it will most probably raise a TypeError too.
Note that if you want to have both the item and index, the solution is the builtin enumerate(sequence), which yields an (index, item) tuple for each item:
array = list("abcdef")
for index, letter in enumerate(array):
print("{} : {}".format(index, letter)
Understood your question, explaining it using a different example.
1. Multiple Assignments using -> Dictionary
dict1 = {1: "Bitcoin", 2: "Ethereum"}
for key, value in dict1.items():
print(f"Key {key} has value {value}")
print(dict1.items())
Output:
Key 1 has value Bitcoin
Key 2 has value Ethereum
dict_items([(1, 'Bitcoin'), (2, 'Ethereum')])
Explaining dict1.items():
dict1_items() creates values dict_items([(1, 'Bitcoin'), (2, 'Ethereum')])
It comes in pairs (key, value) for each iteration.
2. Multiple Assignments using -> enumerate() Function
coins = ["Bitcoin", "Ethereum", "Cardano"]
prices = [48000, 2585, 2]
for i, coin in enumerate(coins):
price = prices[i]
print(f"${price} for 1 {coin}")
Output:
$48000 for 1 Bitcoin
$2585 for 1 Ethereum
$2 for 1 Cardano
Explaining enumerate(coins):
enumerate(coins) creates values ((0, 'Bitcoin'), (1, 'Ethereum'), (2, 'Cardano'))
It comes in pairs (index, value) for each (one) iteration
3. Multiple Assignments using -> zip() Function
coins = ["Bitcoin", "Ethereum", "Cardano"]
prices = [48000, 2585, 2]
for coin, price in zip(coins, prices):
print(f"${price} for 1 {coin}")
Output:
$48000 for 1 Bitcoin
$2585 for 1 Ethereum
$2 for 1 Cardano
Explaining
zip(coins, prices) create values (('Bitcoin', 48000), ('Ethereum', 2585), ('Cardano', 2))
It comes in pairs (value-list1, value-list2) for each (one) iteration.
I just wanted to add that, even in Python, you can get a for in effect using a classic C style loop by just using a local variable
l = len(mylist) #I often need to use this more than once anyways
for n in range(l-1):
i = mylist[n]
print("iterator:",n, " item:",i)

How to return the index of the list item

I have the following list in Python:
x = [[1,2],[3,4],[5,'inf'],[6,7],['nan',10]]
How can I return the index of the items that contain 'inf' or 'nan'?
EDIT-1
The first thing I thought about is to make an if-statement, as follows:
if (float('inf') in i) or (float('nan') in i)
But, didn't know how to go about returning the index value.
EDIT-2
The question that has been marked as duplicate didn't actually answer my question.
Thanks.
you can iterate over the list and check for nan or inf:
x = [[1,2],[3,4],[5,'inf'],[6,7],['nan',10]]
for y in x:
if 'inf' in y:
print(x.index(y),y.index('inf'))
elif 'nan' in y:
print(x.index(y), y.index('nan'))
the result will be:
2 1
4 0
I'm glad you added an attempt! We SO users love when folks show their work per say before asking a question. It shows that you are genuinely care about the problem you're trying to solve and not just here for a free-work service.
That being said, I like the simplicity of a list comprehension here:
>>> [(i, j) for i, seq in enumerate(x) for j, item in enumerate(seq) if item == 'inf' or item == 'nan']
[(2, 1), (4, 0)]
A more concise version of the above that takes advantage of sets and their performant membership checks:
>>> [(i, j) for i, seq in enumerate(x) for j, item in enumerate(seq) if item in {'inf', 'nan'}]
What we do here is (1) iterate through the nested list in a nested order, (2) check to see if our item matches the conditions you laid out, (3) capture the index of that position via the result of our enumerate() call.
Suggested Readings:
Python Strings
Enumerate function
[(i, j) for i, outer in enumerate(x) for j, inner in enumerate(outer ) if inner in ['inf','nan']]

Can't iterate list after filter

When I run this code:
arr = list(filter(lambda x: x > 0, arr))
for index, item in arr:
# do something
I get this error
TypeError: 'int' object is not iterable
Which doesn't make sense because I have a list not an int.
Testing arr yields:
>>> print(arr)
[822]
>>> print(type(arr))
<class 'list'>
>>> print(len(arr))
1
Although there are plenty of questions regarding this type error here none explains this case.
What could possibly be wrong?
The problem is not that filter is not iterable, but rather the fact that you do iterable unpacking in the head of the for loop:
for index, item in arr:
# do something
Since the elements of arr are integers, you can not unpack them in two values (you write index, item as the "target" of the assignment, but how should Python solve index, item = 1?). You can use enumerate(..) to keep track of the index:
for index, item in enumerate(arr):
# do something
enumerate(..) takes as input an iterable, and produces an iterable of tuples where each tuple contains the index (as first item), and the element as second item. So enumerate([1, 'a', 25]) will produce (0, 1), (1, 'a'), (2, 25).
If you do not need the index, you can iterate over the items without an index:
for item in arr:
# do something
If one has difficulty understanding enumerate, one can use following simple workaround:
arr = list(filter(lambda x: x > 0, arr))
for index in range(len(arr)):
item = arr[index]
# do something with item and index
I have not tested it but it may be more efficient than enumerate since it would avoid the overhead.

What does enumerate() mean?

What does for row_number, row in enumerate(cursor): do in Python?
What does enumerate mean in this context?
The enumerate() function adds a counter to an iterable.
So for each element in cursor, a tuple is produced with (counter, element); the for loop binds that to row_number and row, respectively.
Demo:
>>> elements = ('foo', 'bar', 'baz')
>>> for elem in elements:
... print elem
...
foo
bar
baz
>>> for count, elem in enumerate(elements):
... print count, elem
...
0 foo
1 bar
2 baz
By default, enumerate() starts counting at 0 but if you give it a second integer argument, it'll start from that number instead:
>>> for count, elem in enumerate(elements, 42):
... print count, elem
...
42 foo
43 bar
44 baz
If you were to re-implement enumerate() in Python, here are two ways of achieving that; one using itertools.count() to do the counting, the other manually counting in a generator function:
from itertools import count
def enumerate(it, start=0):
# return an iterator that adds a counter to each element of it
return zip(count(start), it)
and
def enumerate(it, start=0):
count = start
for elem in it:
yield (count, elem)
count += 1
The actual implementation in C is closer to the latter, with optimisations to reuse a single tuple object for the common for i, ... unpacking case and using a standard C integer value for the counter until the counter becomes too large to avoid using a Python integer object (which is unbounded).
It's a builtin function that returns an object that can be iterated over. See the documentation.
In short, it loops over the elements of an iterable (like a list), as well as an index number, combined in a tuple:
for item in enumerate(["a", "b", "c"]):
print item
prints
(0, "a")
(1, "b")
(2, "c")
It's helpful if you want to loop over a sequence (or other iterable thing), and also want to have an index counter available. If you want the counter to start from some other value (usually 1), you can give that as second argument to enumerate.
I am reading a book (Effective Python) by Brett Slatkin and he shows another way to iterate over a list and also know the index of the current item in the list but he suggests that it is better not to use it and to use enumerate instead.
I know you asked what enumerate means, but when I understood the following, I also understood how enumerate makes iterating over a list while knowing the index of the current item easier (and more readable).
list_of_letters = ['a', 'b', 'c']
for i in range(len(list_of_letters)):
letter = list_of_letters[i]
print (i, letter)
The output is:
0 a
1 b
2 c
I also used to do something, even sillier before I read about the enumerate function.
i = 0
for n in list_of_letters:
print (i, n)
i += 1
It produces the same output.
But with enumerate I just have to write:
list_of_letters = ['a', 'b', 'c']
for i, letter in enumerate(list_of_letters):
print (i, letter)
As other users have mentioned, enumerate is a generator that adds an incremental index next to each item of an iterable.
So if you have a list say l = ["test_1", "test_2", "test_3"], the list(enumerate(l)) will give you something like this: [(0, 'test_1'), (1, 'test_2'), (2, 'test_3')].
Now, when this is useful? A possible use case is when you want to iterate over items, and you want to skip a specific item that you only know its index in the list but not its value (because its value is not known at the time).
for index, value in enumerate(joint_values):
if index == 3:
continue
# Do something with the other `value`
So your code reads better because you could also do a regular for loop with range but then to access the items you need to index them (i.e., joint_values[i]).
Although another user mentioned an implementation of enumerate using zip, I think a more pure (but slightly more complex) way without using itertools is the following:
def enumerate(l, start=0):
return zip(range(start, len(l) + start), l)
Example:
l = ["test_1", "test_2", "test_3"]
enumerate(l)
enumerate(l, 10)
Output:
[(0, 'test_1'), (1, 'test_2'), (2, 'test_3')]
[(10, 'test_1'), (11, 'test_2'), (12, 'test_3')]
As mentioned in the comments, this approach with range will not work with arbitrary iterables as the original enumerate function does.
The enumerate function works as follows:
doc = """I like movie. But I don't like the cast. The story is very nice"""
doc1 = doc.split('.')
for i in enumerate(doc1):
print(i)
The output is
(0, 'I like movie')
(1, " But I don't like the cast")
(2, ' The story is very nice')
I am assuming that you know how to iterate over elements in some list:
for el in my_list:
# do something
Now sometimes not only you need to iterate over the elements, but also you need the index for each iteration. One way to do it is:
i = 0
for el in my_list:
# do somethings, and use value of "i" somehow
i += 1
However, a nicer way is to user the function "enumerate". What enumerate does is that it receives a list, and it returns a list-like object (an iterable that you can iterate over) but each element of this new list itself contains 2 elements: the index and the value from that original input list:
So if you have
arr = ['a', 'b', 'c']
Then the command
enumerate(arr)
returns something like:
[(0,'a'), (1,'b'), (2,'c')]
Now If you iterate over a list (or an iterable) where each element itself has 2 sub-elements, you can capture both of those sub-elements in the for loop like below:
for index, value in enumerate(arr):
print(index,value)
which would print out the sub-elements of the output of enumerate.
And in general you can basically "unpack" multiple items from list into multiple variables like below:
idx,value = (2,'c')
print(idx)
print(value)
which would print
2
c
This is the kind of assignment happening in each iteration of that loop with enumerate(arr) as iterable.
the enumerate function calculates an elements index and the elements value at the same time. i believe the following code will help explain what is going on.
for i,item in enumerate(initial_config):
print(f'index{i} value{item}')

Returning list elements and indices [duplicate]

This question already has answers here:
'Return' keyword returns only one element from a loop?
(3 answers)
Closed 8 years ago.
I am trying to print all the elements of a list using a user made function.
y = [1,2,3]
def ash(list1):
for x in range(len(list)):
return list[x],x
what i want to do is return all the values in the list with their indices,but all i get is a single element.I can get the elements to print but not return.
You are returning on the very first iteration. Rather, you need to create a list in your function, and add all the tuples in that list, and finally return that list.
And for getting both index and element, you can use enumerate():
def ash(list1):
new_list = []
for x, elem in enumerate(list1):
new_list.append((elem,x))
return new_list
Or, even better you can simply use List comprehension:
return [(elem, x) for x, elem in enumerate(list1)]
The previous two methods creates the list in memory. If you have a very large list to deal with, then you should probably use generators, using yield keyword:
def ash(list1):
for x, elem in enumerate(list1):
yield elem, x
Some issues with your code:
Don't iterate using range unless necessary. Iterate the list directly, or here, use enumerate
Don't use list as a variable - you'll shadow the built-in of the same name. It's confusing to the reader.
You're returning out of the loop. This is why you only get the first iteration. If you want to return successive values, use yield, which turns your function into a generator:
def ash(l):
for x in range(len(l)):
yield l[x],x
This is really a reimplementation of enumerate:
list(enumerate('abc')) #=> [(0, 'a'), (1, 'b'), (2, 'c')]
If you really want to swap the order of the pairs, you can do:
[b,a for a,b in enumerate('abc')]
Alternative implementation: l='abc';zip(l,xrange(len(l)))
enumerate(list) is what you're looking for. (see doc). Also, calling return will give you only the first value of the list when calling the function, what you want here is probably the yield statement:
def ash(list):
for i,item in enumerate(list):
yield item,i
if __name__ == '__main__':
y = [1,2,3]
ys_with_indices = list(ash(y))
print ys_with_indices
Note that this will return a generator object, which you have to convert to a list by calling list() on it. Alternatively, just use a normal list that you append the individual values to:
def ash(list):
items_with_indices = []
for i,item in enumerate(list):
items_with_indices.append((item,i))
return items_with_indices
if __name__ == '__main__':
y = [1,2,3]
ys_with_indices = ash(y)
print ys_with_indices
def ash(lst):
return [(lst[x],x) for x in range(0,len(lst))]
You will get a list of tuples where the first value of the tuple is an element of the original list and the second the index of the element in the list.
For y = [1,2,3] the result is [(1, 0), (2, 1), (3, 2)]

Categories