2019-05-28 01:21:27 (edited by amerikranian 2019-05-28 01:23:06)

Suppose we have the following code.

x=[1,2,3,4,5,6,7,8,9,3,3,4,5,4,3]
x=[item for item in x if item!=3]

Simple enough, right? We are removing an element from the list if it is equal to 3. However, this code will produce a traceback:

x=[1,2,3,4,5,6,7,8,9,3,3,4,5,4,3]
x=[a for item in x if item!=3]

Running this will yield the following bit:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
NameError: name 'a' is not defined

The same will happen if I run the code that looks like this:

x=[1,2,3,4,5,6,7,8,9,3,3,4,5,4,3]
x=[a for item in x if a!=3]

The code will only work if I keep the same naming throughout the line, that is, [item for item in x if item!=3].
So, why? Why does the code throw a traceback when trying to do the 2 situations listed after the working solution? What is happening under the hood after I type in the first 2 lines of code? Are there any more similar situations in which the same constant rule would apply?

2019-05-28 02:02:16 (edited by stewie 2019-05-28 02:05:39)

you use [a for item instead of [item for item. Think of it like a for loop, the variable being referred to has to be the one looping through the list. Also keep in mind that you aren't removing anything from the list when using a list comprehension. In this case you're building a copy of the old list that does not contain 3.

Deep in the human unconscious is a pervasive need for a logical universe that makes sense. But the real universe is always one step beyond logic.

2019-05-28 03:55:42

Right, but what happens to all the elements that contain three? Are they simply gone?  To put it in another way, what is the difference between building a copy of a list and removing an item from it

2019-05-28 03:57:03

So I have my own question here.
The code above looks very, very compact.
Why not use this?
l=[1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1]
for x in l:
if(x==3):
  l.remove(x)

I mean you can also do:
while(3 in l):
l.remove(3)

but eh whatever. Same difference.

Nathan Smith
Managing Director of Nathan Tech
It's not disability
It's ability!

2019-05-28 04:03:13

ok, sorry for the double post, but I really, still don't get what the purpose is.
Like, I'm reading the code and... just not getting it.
Maybe this is a learning thing for me.
Are you saying list1 is this, and you want list2 to be list1 but without the 3's?

I think the shorthand confused me, again, sorry for the double post

Nathan Smith
Managing Director of Nathan Tech
It's not disability
It's ability!

2019-05-28 04:57:26 (edited by stewie 2019-05-28 04:58:19)

@5 That is what is happening

@3 That code is a list comprehension, which is kind of a for loop. The original list is not being changed directly by the list comprehension at all. This should be clearer with a slightly modified example.

x = [1,2,3,4,5,6,7,8,9,3,3,4,5,4,3]
y = [item for item in x if item!=3]

The list comprehension is only interacting with the original because you told it to (by referencing x directly). All this is doing is repeating the item variable through x. If item is not equal to 3, it adds the number from x to the new list. If item is 3, it just does nothing because the condition if item != 3 is false. The two lists (x and y) are two completely different objects in the same way as two ints are different, even though they may hold the same number as a value. To remove all the 3's from x directly, you'd have to call some method on x, such as x.remove(3) multiple times or do it some other way.

The reason the list changed originally in your example was because you set x equal to the new list.

Deep in the human unconscious is a pervasive need for a logical universe that makes sense. But the real universe is always one step beyond logic.

2019-05-28 05:15:04

So is that a bad thing? Why would I want to use some other  method?  When would you use list comprehension?

2019-05-28 10:38:46

@4 is really a preference really.
List compreensions are quick ways to create new lists from an other list in one line.
Suppose we have this list
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16]
Instead of spending more than 3 lines of code to do an action such as removing elements from the list you can just generate the new list from the old list.
Soinstead of doing
for item in x:
if item>3:
  x.remove(item)
you can just do it in one line
x = [item for item in x if item>3]
This code is equivalent to:
def compreension(x):
for item in x:
  if item>3:
   x.remove(item)
return x
You are not forced to use it. It is a preference really.

Paul

2019-05-28 14:30:10

I think this question is less about syntax and more about fundamentals of programming.
Whenever you create a list in memory, you assign it a reference variable. If you make a modification to that object in memory, what you're really doing is copying its data in tandem with whatever change you made and creating a new object in memory. Reference variables point to memory addresses rather than objects (or data to make this less confusing). This is seemless to us, and it's all under the hood so to speak. Take the the following code:

x = [1, 2, 3, ...]
y = x

Both x and y reference the same list. All you did was assign y to the same memory address that x is assigned to.
I highly recommend looking into the object-oriented side of programming, especially if you're using any of the more modern-day languages like Python.

What game will hadi.gsf want to play next?

2019-05-29 13:10:15

List comprehension generates a new list, it doesn't modify the original. It's useful if you want a subset of an existing list without wanting to modify that list. If you assign the new list to the same variable though then you could just as well remove it from the original, it doesn't really matter. Post 9 also has a good point, saying y=x in that example won't actually create a copy of x, it will only create a new reference to x. So if you modify y, the same will happen to x. If you want to create a new copy you need to do something like y =x[:], where the colon is used to slice lists (but using it without any other parameters will return the whole list).

2019-05-29 15:34:37

Part of what bakes this tricky is that lists or arrays are actually pointers. Even though python touts the fact its a high level language that you don't have to worry about pointers and memory, in reality you kind of still do.
personally i still like the more verbose version. its easier to read and much clearer as to what is actually going on. this goes for new programmers if you are collaborating with people and yourself 6 months or a ear down the road when you try and comprehend what the hell you wrote that one night. also good commenting can solve this too. if you are finding yourself writing a comment to explain what a tricky or clever bit or code does, there almost always will be a clearer way to write it. comments should explain why you wrote code like you did and your thought processes behind it.

I don’t believe in fighting unnecessarily.  But if something is worth fighting for, then its always a fight worth winning.
check me out on Twitter and on GitHub

2019-05-30 00:15:38

So paul, I just gotta give ya a friendly little poke here. Your examples are a bit broken, I figured you'd find it pretty amusing if I pointed it out big_smile So your first one, for item in x: if item>3: x.remove(item) will remove every other item that is greater than 3. x = [item for item in x if item>3] will keep every item in the list that is greater than 3 and remove everything that is less than or equal to 3. I'm guessing in your first example with the if statements you were trying to remove everything greater than 3 and not every other thing, and I'm assuming you wanted to do the same in your second example big_smile so I believe you wanted this.
x=[1,2,3,4,5,6,7,8,9,10,11,12,13]
#or, this ones for everybody big_smile
x=list(range(1,14))
#example 1
for item in list(x):
    if item>3: x.remove(item)
#example 2
x = [item for item in x if item<=3]
Anyway this isn't me trying to be cocky or anything to anyone who would think so, I just know that if I did this Paul would also commence some friendly trolling in my direction big_smile

I am a web designer, and a game developer. If you wish see me at http://www.samtupy.com

2019-05-30 01:33:26

@amerikranian: I think it may be okay to say coding can be hard at times. big_smile

What game will hadi.gsf want to play next?

2019-05-30 02:30:49

My signature is half sarcasm, half truth. While coding it’s self is not hard, you can learn the syntax in just a couple of days, it takes skill to put it all together and understand how to apply the concepts to whatever you want to do.  Furthermore, you are adding another layer of difficulty when allowing the user to input data, as they might not enter exactly what you want.

2019-05-30 03:09:06

Oh, I understand what your signature means, but it was a little humorous to read "coding is not hard!" and then see a plethora of replies about how to do something with code. Lol.

What game will hadi.gsf want to play next?

2019-05-30 06:30:56

sam, Thanks. Yes you're damn right.
Talk about reading your posts before posting. Hmmmmm.

Paul

2019-05-31 03:49:12

ok. But what about nested list comprehentions?  do they have their uses?

2019-06-01 15:50:01

I think if you want to use nested list comprehensions, you should really write the longer versions with for and if. While it might seem very cool to create a list and then imediately create another out of that first list in one single line, you'll never understand your code ever again smile Besides, most times, you can avoid nested lc's by simply using more conditional statements in one lc.

We are pleased, that you made it through the final challenge, where we pretended we were going to murder you. We are throwing a party in honor of your tremendous success. Place the device on the ground, then lay on your stomach with your arms at your sides. A party associate will arrive shortly to collect you for your party. Assume the party submission position or you will miss the party.

2019-06-01 23:28:52

yeah. i noticed it while i was going through the python documentation. Honestly it seems that list comprehentions aren't that useful because you can only add and remove things in a list by making a new list. I think that if you're going to add something to a list you better off using the append method or if you're going to combine 2 lists together just use the extend methods. It might be good to use tlist comprehentions if your'e going to do really simple expressions with them but once you want to do more complicated code you're just better off writing stuff on seperat lines and using the built in methods for lists.
Feel free to correct me if i'm wrong.

2019-06-02 05:37:50

@Post 1, that's because what you're doing is a generator.
x = [item for item in x if item!= 3]
Means:
Give me a list wherein we iterate x. The iterator will be called item. So, let's put item in the list for items that do not equal 3.

If you have issues with Scramble, please contact support at the link below. I check here at least once a day, so this is the best avenue for submitting your issues and bug reports.
https://stevend.net/scramble/support

2019-06-05 22:28:35

why do you use loop inside brackets?

2019-06-05 23:20:21

Here's another way to generate the same list. It may be more useful if you have a big list or if you have a big function to filter the elements.
x = [1,2,3,4,5,6,7,8,9,3,3,4,5,4,3]
y = filter(lambda value: value != 3, x)

The filter function works calling the first parameter passed to it (the lambda function in this case). The lambda function returns true if x is not equal to 3. If it's 3, the lambda function returns false and filter won't include it in the new iterator.

2019-06-06 03:11:22 (edited by Ethin 2019-06-06 03:14:47)

@20, this is not an example of a generator at all. A generator is an actual class -- class Generator to be exact. A generator requires that you call next() on it repeatedly until you get a StopIteration exception, which is the generators way of telling you "OK! I'm done!" An example of a generator is:

def grange(max):
    for i in range (max):
        yield i

If I run this in my Python console, I get:
<generator object grange at 0x00000250F3D127C8>
That is what a generator looks like. a list comprehension is not a generator because if you enter something like
[i for i in range (30)]
or
[i for i in range (10000) if i %2 == 0]
You will get back a list, not a generator. You could transform this generator into a list, but that would not only defeat the purpose of generators but be horribly inefficient, especially on massive generator sets. There are lots of places to learn about generators, so if your interested in them, go check some tutorials out.

"On two occasions I have been asked [by members of Parliament!]: 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out ?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question."    — Charles Babbage.
My Github

2019-06-07 19:33:43 (edited by majno 2019-06-07 21:31:48)

Be careful when removing items using for loops.
if you do some thing like this.
x [1, 2, 3, 4, 5, 6, 7]
for item in x:
  if item == 3 x.remove(item)

the index will skp the next element, so 4 will not be checked. if you have
x = [1, 2, 3, 3, 4, 5]
will only remove the first number 3 element in the list.
for those cases is better use list comprehension instead for loops.

2019-06-07 20:27:27 (edited by Ethin 2019-06-07 20:28:45)

@24, this is not correct. Running this in my python environment:
>>> x = [1, 2, 3, 4, 5, 6, 7]
>>> for item in x:
...  if item != 3:
...   x.remove(item)
...
>>> x
[2, 3, 5, 7]
if I use your other list (x = [1, 2, 3, 3, 4, 5]):
>>> x = [1, 2, 3, 3, 4, 5]
>>> for item in x:
...  if item != 3:
...   x.remove(item)
...
>>> x
[2, 3, 3, 5]
Case in point: all items are checked against all conditions and branches in the loop unless continue or break is executed in one of the conditional branches and that particular branches condition happens to be true.

"On two occasions I have been asked [by members of Parliament!]: 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out ?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question."    — Charles Babbage.
My Github