59

I have a issue with python.

I make a simple list:

>>> my_list = ["one","two","three"]

I want create a "single line code" for find a string.

for example, I have this code:

>>> [(i) for i in my_list if i=="two"]
['two']

But when I watch the variable is wrong (I find the last value of my list):

>>> print i
three

Why does my variable contain the last element and not the element that I want to find?

gboffi
  • 22,939
  • 8
  • 54
  • 85
rostonik
  • 639
  • 1
  • 7
  • 8
  • 4
    If you want to find a list of items matching some criteria, then use `[i for i in my_list if ...]`. If you want to find one item matching some criteria, consider using [`next`](https://docs.python.org/2/library/functions.html#next); e.g. `x = next(i for i in my_list if ...)` – khelwood Sep 15 '15 at 07:50
  • i will not contain the value, it is still assigned all of the values. The result will only contain the values that meet the criteria. – matt Sep 15 '15 at 07:51
  • you are not wrong and your interpreter is not wrong it is just that the loop goes till the last value even if the condition is not satisfied so you are getting "three" to avoid that you could do what @khelwood said – The6thSense Sep 15 '15 at 07:51
  • Why aren't you using `"two" in mylist` (which returns a boolean) or `mylist.index("two")` (which returns the position of the found string)? Since you already have the string you're looking for, it's pointless to have it returned yet again by the search, isn't it? – Tim Pietzcker Sep 15 '15 at 08:24

8 Answers8

74

You are producing a filtered list by using a list comprehension. i is still being bound to each and every element of that list, and the last element is still 'three', even if it was subsequently filtered out from the list being produced.

You should not use a list comprehension to pick out one element. Just use a for loop, and break to end it:

for elem in my_list:
    if elem == 'two':
        break

If you must have a one-liner (which would be counter to Python's philosophy, where readability matters), use the next() function and a generator expression:

i = next((elem for elem in my_list if elem == 'two'), None)

which will set i to None if there is no such matching element.

The above is not that useful a filter; your are essentially testing if the value 'two' is in the list. You can use in for that:

elem = 'two' if 'two' in my_list else None
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
66

When you perform

>>> [(i) for i in my_list if i=="two"]

i is iterated through the list my_list. As the list comprehension finishes evaluation, i is assigned to the last item in iteration, which is "three".

Cong Ma
  • 10,692
  • 3
  • 31
  • 47
  • 1
    Hi, i resolve this issue with this code: res = [(i) for i in my_list if i=="two"] ! It's correct? – rostonik Sep 15 '15 at 07:58
  • 1
    I think it is worth mentioning that `i` is only assigned to the last item in old (pre 3.x) versions of Python. In Python 3.x the variable scope is limited to inside the list comprehension so outside the comprehension you will see the previous value of `i`, or it will be undefined if it had no previous value. – Duncan Sep 15 '15 at 08:05
  • 1
    @Cong Ma you rejected an edit, that actually addressed the issue OP wasn't assigning the result to a value. – matt Sep 15 '15 at 08:10
  • He asked "Why ..." and this is an answer. It's up to himself whether to assign this to anything if at all. – Cong Ma Sep 15 '15 at 08:16
  • 4
    how about 'else'? – Dee Apr 12 '21 at 02:51
24

In list comprehension the loop variable i becomes global. After the iteration in the for loop it is a reference to the last element in your list.

If you want all matches then assign the list to a variable:

filtered =  [ i for i in my_list if i=='two']

If you want only the first match you could use a function generator

try:
     m = next( i for i in my_list if i=='two' )
except StopIteration:
     m = None
desiato
  • 1,122
  • 1
  • 9
  • 16
7

Short answer:

  1. one line loop with if statement
my_list = [1, 2, 3]
[i for i in my_list if i==2]
  1. one line loop with both if and else statement. unfortunately, we can't put if-else statement in the end like above.
[i if i==2 else "wrong" for i in my_list]
Jin
  • 141
  • 2
  • 4
5

In python3 the variable i will be out of scope when you try to print it.

To get the value you want you should store the result of your operation inside a new variable:

my_list = ["one","two","three"]
result=[ i for i in my_list if i=="two"]
print(result)

you will then get the following output

['two']
3

The reason it prints "three" is because you didnt define your array. The equivalent to what you're doing is:

arr = []
for i in array :
    if i == "two" :
        arr.push(i)
print(i)

You are asking for the last element it looked through, which is not what you should be doing. You need to be storing the array to a variable in order to get the element.

The english equivalent of what you are doing is:

You: "I need you to print all the elements in this array that equal two, but in an array. And each time you cycle through the list, define the current element as I."
Computer: "Here: ["two"]"
You: "Now tell me 'i'"
Computer: "'i' is equal to "three"
You: "Why?"

The reason 'i' is equal to "three" is because three was the last thing that was defined as I

the computer did:

i = "one"
i = "two"
i = "three"

print(["two"])

Because you asked it to.

If you want the index, go here If you want the values in an array, define the array, like this:

MyArray = [(i) for i in my_list if i=="two"]
MartinNajemi
  • 514
  • 3
  • 18
1

Found this one:

[x for (i,x) in enumerate(my_list) if my_list[i] == "two"]

Will print:

["two"]
0

your code

[(i) for i in my_list if i=="two"]

equals to

a=[]
for i in my_list:
  if i=="two":
    a.append(i)

so it iterates entire my_list and hit last item "three" but only returning what equals to "two" and put in a new list

Ziyi Chen
  • 101
  • 3