[Tutor] Query: lists
when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below function, it picks up only 'xzz' and not 'xaa' def front_x(words): # +++your code here+++ a = [] b = [] for z in words: if z.startswith('x'): words.remove(z) b.append(z) print 'z is', z print 'original', sorted(words) print 'new', sorted(b) print sorted(b) + sorted(words) Thanks, Deepti ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
On 14/08/18 09:11, Deepti K wrote: > when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below > function, it picks up only 'xzz' and not 'xaa' Correct because > def front_x(words): > # +++your code here+++ > a = [] > b = [] > for z in words: > if z.startswith('x'): > words.remove(z) You just changed the thing you are iterating over. By removing an elem,ent the list got shorter so the internal counter inside the for loop now points at the next item - ie it skipped one. As a general rule never modify the thing you are iterating over with a for loop - use a copy or change to a while loop instead. > b.append(z) > print 'z is', z > print 'original', sorted(words) But it's not the original because you've removed some items. > print 'new', sorted(b) > print sorted(b) + sorted(words) But this should be the same as the original (albeit almost sorted). PS. Since you only modify 'b' and 'words' you don't really need 'a' -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
On 14Aug2018 18:11, Deepti K wrote: when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below function, it picks up only 'xzz' and not 'xaa' def front_x(words): # +++your code here+++ a = [] b = [] for z in words: if z.startswith('x'): words.remove(z) b.append(z) print 'z is', z print 'original', sorted(words) print 'new', sorted(b) print sorted(b) + sorted(words) That is because you are making a common mistake which applies to almost any data structure, but is particularly easy with lists and loops: you are modifying the list _while_ iterating over it. After you go: words.remove(z) all the elements _after_ z (i.e. those after 'xzz' i.e. ['xaa']) are moved down the list. In your particular case, that means that 'xaa' is now at index 3, and the next iteration of the loop would have picked up position 4. Therefore the loop doesn't get to see the value 'xaa'. A "for" loop and almost anything that "iterates" over a data structure does not work by taking a copy of that structure ahead of time, and looping over the values. This is normal, because a data structure may be of any size - you do not want to "make a copy of all the values" by default - that can be arbitrarily expensive. Instead, a for loop obtains an "iterator" of what you ask it to loop over. The iterator for a list effectively has a reference to the list (in order to obtain the values) and a notion of where in the list it is up to (i.e. a list index, a counter starting at 0 for the first element and incrementing until it exceeds the length of the list). So when you run "for z in words", the iterator is up to index 3 when you reach "xzz". So z[3] == "xzz". After you remove "xzz", z[3] == "xaa" and in this case there is no longer a z[4] at all because the list is shortened. So the next loop iteration never inspects that value. Even if the list had more value, the loop would still skip the "xaa" value. You should perhaps ask yourself: why am I removing values from "words"? If you're just trying to obtain the values starting with "x" you do not need to modify words because you're already collecting the values you want in "b". If you're trying to partition words into values starting with "x" and values not starting with "x", you're better off making a separate collection for the "not starting with x" values. And that has me wondering what the list "b" in your code was for originally. As a matter of principle, functions that "compute a value" (in your case, a list of the values starting with "x") should try not to modify what they are given as parameters. When you pass values to Python functions, you are passing a reference, not a new copy. If a function modifies that reference's _content_, as you do when you go "words.move(z)", you're modifying the original. Try running this code: my_words = ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] print 'words before =", my_words front_x(my_words) print 'words after =", my_words You will find that "my_words" has been modified. This is called a "side effect", where calling a function affects something outside it. It is usually undesirable. Cheers, Cameron Simpson ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
Alan Gauld via Tutor wrote: > On 14/08/18 09:11, Deepti K wrote: >> when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below >> function, it picks up only 'xzz' and not 'xaa' > > Correct because > >> def front_x(words): >> # +++your code here+++ >> a = [] >> b = [] >> for z in words: >> if z.startswith('x'): >> words.remove(z) > > You just changed the thing you are iterating over. > By removing an elem,ent the list got shorter so the > internal counter inside the for loop now points at > the next item - ie it skipped one. > > As a general rule never modify the thing you are > iterating over with a for loop - use a copy or > change to a while loop instead. > > >> b.append(z) >> print 'z is', z >> print 'original', sorted(words) > > But it's not the original because you've removed > some items. > >> print 'new', sorted(b) >> print sorted(b) + sorted(words) > > But this should be the same as the original > (albeit almost sorted). > > PS. Since you only modify 'b' and 'words' you > don't really need 'a' For a simple solution you do need a and b: leave words unchanged, append words starting with "x" to a and words not starting with "x" to b. Someone familiar with Python might do it with a sort key instead: >>> sorted(['bbb', 'ccc', 'axx', 'xzz', 'xaa'], ... key=lambda s: not s.startswith("x")) ['xzz', 'xaa', 'bbb', 'ccc', 'axx'] If you want ['xaa', 'xzz', 'axx', 'bbb', 'ccc'] as the result you can achieve that by sorting twice (Python's sorting is "stable") or by tweaking the key function. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
Thanks all. This is very helpful. I am new to Python :) Sent from my iPhone > On 15 Aug 2018, at 8:16 am, Peter Otten <__pete...@web.de> wrote: > > Alan Gauld via Tutor wrote: > >>> On 14/08/18 09:11, Deepti K wrote: >>> when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below >>> function, it picks up only 'xzz' and not 'xaa' >> >> Correct because >> >>> def front_x(words): >>> # +++your code here+++ >>> a = [] >>> b = [] >>> for z in words: >>>if z.startswith('x'): >>> words.remove(z) >> >> You just changed the thing you are iterating over. >> By removing an elem,ent the list got shorter so the >> internal counter inside the for loop now points at >> the next item - ie it skipped one. >> >> As a general rule never modify the thing you are >> iterating over with a for loop - use a copy or >> change to a while loop instead. >> >> >>> b.append(z) >>> print 'z is', z >>> print 'original', sorted(words) >> >> But it's not the original because you've removed >> some items. >> >>> print 'new', sorted(b) >>> print sorted(b) + sorted(words) >> >> But this should be the same as the original >> (albeit almost sorted). >> >> PS. Since you only modify 'b' and 'words' you >> don't really need 'a' > > For a simple solution you do need a and b: leave words unchanged, append > words starting with "x" to a and words not starting with "x" to b. > > Someone familiar with Python might do it with a sort key instead: > sorted(['bbb', 'ccc', 'axx', 'xzz', 'xaa'], > ... key=lambda s: not s.startswith("x")) > ['xzz', 'xaa', 'bbb', 'ccc', 'axx'] > > If you want ['xaa', 'xzz', 'axx', 'bbb', 'ccc'] as the result > you can achieve that by sorting twice (Python's sorting is "stable") or by > tweaking the key function. > > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
Deepti, What you’re seeing happens because you are making changes (words.remove(z)) to the list while you are iterating over it (for z in words). If your goal is to print the original words, removed words and original words without removed words, you could do something like this using sets: words = ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] def front_x(words): # +++your code here+++ removed_words = [] for word in words: if word.startswith('x'): removed_words.append(word) print 'word is', word print 'original', sorted(words) print 'new', sorted(removed_words) print sorted(list(set(removed_words + words))) If you also wanted to get the original words after removing the removed_words, you could print them like this: print 'original - new', list(set(words) - set(removed_words)) Or you could even use list comprehension like this: print 'original - new', [word for word in words if word not in removed_words] -- Thanks, Nitin Madhok Clemson University CONFIDENTIALITY NOTICE This e-mail, and any attachments thereto, is intended only for use by the addressee(s) named herein and may contain privileged and/or confidential information. If you are not the intended recipient of this e-mail, any dissemination, distribution or copying of this e-mail, and any attachments thereto, is strictly prohibited. If you have received this e-mail in error, please immediately notify the sender by e-mail or telephone and permanently delete all copies of this e-mail and any attachments. > On Aug 14, 2018, at 4:11 AM, Deepti K wrote: > > when I pass ['bbb', 'ccc', 'axx', 'xzz', 'xaa'] as words to the below > function, it picks up only 'xzz' and not 'xaa' > > def front_x(words): > # +++your code here+++ > a = [] > b = [] > for z in words: >if z.startswith('x'): > words.remove(z) > b.append(z) > print 'z is', z > print 'original', sorted(words) > print 'new', sorted(b) > print sorted(b) + sorted(words) > > Thanks, > Deepti > ___ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
On 14/08/18 23:16, Peter Otten wrote: > For a simple solution you do need a and b: leave words unchanged, append > words starting with "x" to a and words not starting with "x" to b. > > Someone familiar with Python might do it with a sort key instead: Or, for one definition of simple, a list comprehension? filtered_list = [word for word in words if not word.startswith('x')] Of course it doesn't retain the deleted words if that is important. Or if you only want the deleted words simply remove the 'not'. If you need all three results (original, filtered and removed) then you need the original 'a' and 'b' lists as well as the original 'words'. It all depends on what exactly you need as an end result. -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Query: lists
On 14/08/18 22:38, Cameron Simpson wrote: > If you're trying to partition words into values starting with "x" and values > not starting with "x", you're better off making a separate collection for the > "not starting with x" values. And that has me wondering what the list "b" in > your code was for originally. And further to Cameron's point this demonstrates why choosing meaningful variable names (rather than single letters) is so important. If a,b and z had been names expressing their purpose we would be better able to guess at your intentions and ultimate goal. But with single letters we have no real clue. > As a matter of principle, functions that "compute a value" (in your case, a > list of the values starting with "x") should try not to modify what they are > given as parameters. When you pass values to Python functions, you are > passing > a reference, not a new copy. If a function modifies that reference's > _content_, > as you do when you go "words.move(z)", you're modifying the original. It's also good if functions that compute a value *return* that value rather than (or as well as) print it. The act of returning something also helps to clarify the functions objective. With multiple print statements we are not quite sure which line of output is the most important. -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor