Related
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}')
Below is my input
x = ['P' 'AE2' 'A' 'AE1' 'B' 'C']
My function is to return the last value within x that contains 0, 1, or 2 at the end of the value
i.e. 'AE2' or 'AE1'
and then any remaining values
Desired Output
y = ['AE1' 'B' 'C']
The values of x can vary thus the values of y will vary as well. I'm struggling on how to create a function for this problem.
Use this:
reversed()
It should work.
You can access the index this way:
for i, e in reversed(list(enumerate(a))):
print i, e
prints
2 baz
1 bar
0 foo
You do not need a loop, Thats the fun of python.
You can access list items easily
li[:] - This access the item 0 - nth.
li[x:] - Accesses x place till end. (x position could be 2,3 or any)
li[::-1] - Reverses the list.
You can assign the manipulated list to a new list.
new_list = li[0:3] (It will store list item 0 - 3 index in the new_list)
Dig more about list manipulation in python, you would know more.
Simply move backwards:
x= ['P', 'AE2', 'A', 'AE1', 'B', 'C']
for index in range(len(x)-1,-1,-1):
if x[index][-1] in "012":
print(x[index:])
break
['AE1', 'B', 'C']
You can use itertools.accumulate to do this in O(n)
from itertools import accumulate
x = ['P', 'AE2', 'A', 'AE1', 'B', 'C']
list(accumulate(x, lambda p,n: [n] if n[-1] in '012' else list(p)+[n]))[-1]
['AE1', 'B', 'C']
If you're a beginner, it might be good to try keep it as simple as possible. Divide the problem into three sections:
Find the index of the last element that match our condition.
Confirm that such an element actually exist!
Print the elements.
First section suggests that you have to loop through the list and check against all elements. Since you also need the index, as well as the element, it seems like you need to use a for loop with enumerate. What this do is to return the index of the element and the element itself. This is common to do in Python.
Next you need to check if the element ends with what you want. Luckily, Python's strings has a method called endswith that returns True if a string ends with any of the values passed to it.
Lastly, you just need to print from the index you've found and onward! Here's a more concrete example:
x = ['P', 'AE2', 'A', 'AE1', 'B', 'C']
matches = ('0', '1', '2')
# Gets the index of the last element that ends with 0, 1 or 2. We start off by going through each element one by one,
# and if the element match our condition, we save the element's index (the variable i). If we later find another element
# in the list we just override the current index.
index = -1
for i, element in enumerate(x):
if element.endswith(matches):
index = i
# If index still is -1, that means no elements matched our condition.
if index == -1:
print('We did not find any element that ended in 0, 1 or 2!')
else:
# Print all contents of x from index to the end.
print(x[index:])
Few questions on the below code to find if a list is sorted or not:
Why did we use lambda as key here ? Does it always mean key of a list can be derived so ?
In the enumerate loop , why did we compare key(el) < key(lst[i]) and not key(el) <key(el-1) or lst[i+1] <lst[i] ?
def is_sorted(lst, key=lambda x:x):
for i, el in enumerate(lst[1:]):
if key(el) < key(lst[i]): # i is the index of the previous element
return False
return True
hh=[1,2,3,4,6]
val = is_sorted(hh)
print(val)
(NB: the code above was taken from this SO answer)
This code scans a list to see if it is sorted low to high. The first problem is to decide what "low" and "high" mean for arbitrary types. Its easy for integers, but what about user defined types? So, the author lets you pass in a function that converts a type to something whose comparison works the way you want.
For instance, lets say you want to sort tuples, but based on the 3rd item which you know to be an integer, it would be key=lambda x: x[2]. But the author provides a default key=lamba x:x which just returns the object its supplied for items that are already their own sort key.
The second part is easy. If any item is less than the item just before it, then we found an example where its not low to high. The reason it works is literally in the comment - i is the index of the element directly preceding el. We know this because we enumerated on the second and following elements of the list (enumerate(lst[1:]))
enumerate yields both index and current element:
for i, el in enumerate(lst):
print(i,el)
would print:
0 1
1 2
2 3
3 4
4 6
By slicing the list off by one (removing the first element), the code introduces a shift between the index and the current element, and it allows to access by index only once (not seen as pythonic to use indexes on lists when iterating on them fully)
It's still better/pythonic to zip (interleave) list and a sliced version of the list and pass a comparison to all, no indices involved, clearer code:
import itertools
def is_sorted(lst, key=lambda x:x):
return all(key(current) < key(prev) for prev,current in zip(lst,itertools.islice(lst,1,None,None)))
The slicing being done by islice, no extra list is generated (otherwise it's the same as lst[1:])
The key function (here: identity function by default) is the function which converts from the value to the comparable value. For integers, identity is okay, unless we want to reverse comparison, in which case we would pass lambda x:-x
The point is not that the lambda "derives" the key of a list. Rather, it's a function that allows you to choose the key. That is, given a list of objects of type X, what attribute would you use to compare them with? The default is the identity function - ie use the plain value of each element. But you could choose anything here.
You could indeed write this function by comparing lst[i+1] < lst[i]. You couldn't however write it by comparing key(el) < key(el-1), because el is the value of the element itself, not the index.
This is a function that test if a list has been sorted, as an example with the builtin sorted function. This function takes an keyword argument key which is used on every single element on the list to compute its compare value:
>>> sorted([(0,3),(1,2),(2,1),(3,0)])
[(0, 3), (1, 2), (2, 1), (3, 0)]
>>> sorted([(0,3),(1,2),(2,1),(3,0)],key=lambda x:x[1])
[(3, 0), (2, 1), (1, 2), (0, 3)]
The key keyword in your function is to be able to mimic the behavior of sorted:
>>> is_sorted([(0,3),(1,2),(2,1),(3,0)])
True
>>> is_sorted([(0,3),(1,2),(2,1),(3,0)],key=lambda x:x[1])
False
The default lambda is just there to mimic a default behavior where nothing is changed.
I have a sorted list based on first element:
A = [(0.11, '201405'), (0.41, '201402'),.....,(1.5, '201430')] # values and time
and want to change first element of each tuple:
x = len(C) # C is a list and its length is 2
y = len(D) # D is a list and its length is 1
if x > y:
A[0[0:x]] = 0.0 # if x > y then set first element of A equal to zero (over length of C)
But I get following error:
TypeError: 'int' object is not subscriptable
Please suggest to fix it.
If I understand your question correctly, you want to replace the first element in each of the first few tuples with 0. Tuples are not modifyable, so you can not modify the existing tuples in the list, but you have to create a new list, holding new tuples. You can do this using a list comprehension.
Example:
>>> A = [(1,"a"), (2,"b"), (3,"c"), (4,"d")]
>>> A = [(x if i > 1 else 0, y) for i, (x, y) in enumerate(A)]
>>> print A
[(0, 'a'), (0, 'b'), (3, 'c'), (4, 'd')]
This will enumerate all the entries in the list, and for each entry create a tuple with the first element being 0, if the index i of that entry is lower than some threshold, or the original value otherwise.
As iharob mentioned, you have a subscript notation error.
Tuples are immutable. You can't just replace 1 element of the tuple in your list. You have to replace the whole tuple with another tuple containing 0.0 as first value and the existing second value. Note that lists are modifiable but tuples aren't. That's why you can update the list A, but you have to replace the tuple with a new tuple.
Here's an example that does not recreate the whole list:
for i, t in enumerate(A):
if i < 1:
A[i] = (0, t[1])
You have an error in A[0[0:x]] = 0.0 you are using subscript notation for the number 0.
Furthermore, the tuple is an immutable type. Hence, it is impossible to change the content of a tuple. You'd better to generate a new list such as:
A = [0.0]+A[1:]
I want to print my output in lowercase, but I am not getting the correct results. Here's my code. Please help!
import csv
mags = csv.reader(open("mags.csv","rU"))
for row in mags:
print [row[index] for index in (1, 0)]
print [item.lower( ) for item in row]
List comprehension can be nested, like so
print [item.lower() for item in [row[index] for index in (1, 0)]]
Don't have an interpreter handy to test this, tho.
You can nest the two comprehensions like this:
print [item.lower() for item in [row[index] for index in (1, 0)]]
That way you'll use the data from the first comprehension (the second and first item of the row in this order) as input for the second one (lowercase everything).
You can also slice the row instead of using a comprehension for the first one:
print [item.lower() for item in row[::-1][-2:]] # Slicing in 2 steps: [::-1] reverses the list and [-2:] returns the last two items of the reversed list
or (Shorter, but reversed indices slicing doesn't work as you'd think)
print [item.lower() for item in row[1::-1] # Same thing, but it helps to break these things up into steps
Are you sure that the row is a list of strings here ?
I see correct output :
>>> row = [ "alpA", "bETA","gammA" ]
>>> print [item.lower( ) for item in row]
['alpa', 'beta', 'gamma']
>>>