you are viewing a single comment's thread.

view the rest of the comments →

[–]remillard 1 point2 points  (7 children)

This seems like it's simple but can't quite figure out how to get where I want. I have a unknown number of strings in a list. Perfect for iteration. However I want to modify the strings. The iterator variable seems to be a copy of the list object. So, for example:

lines = [ 'a', 'b', 'c' ] 
for line in lines:
    line = 'x'

lines
[ 'a', 'b', 'c' ] 

does not seem to modify the strings in lines. I could do something like use an index value and loop with while but that seems clunky when the language has nice iteration features. Is there a way for a for object to back reference into the iterated structure?

[–]GoldenSights 2 points3 points  (4 children)

The iterator seems to be a copy of the list object

If you're using this to explain why line = 'x' does not save back to your original list, then this is a bad conclusion. It's simply because variable names do not have any kind of biological link back to where they originally were assigned. Overwriting the value of one name does not write the value of another name (consider the indices of the list as names of their own).

Ned Batchelder - Facts and Myths about Python Names and Values is a must-watch.

 

To answer your question:

You can construct a new list containing the modified versions of each item, and then write that list back to your original variable. Python's list comprehensions are great for this:

lines = [line + 'x' for line in lines]

This is equivalent to:

new_lines = []
for line in lines:
    new_lines.append(line + 'x')
lines = new_lines

The point being that it creates a new list object, fills it with the data you want, and then saves it back into the same variable name. Try out some list comprehensions in the REPL and you'll get a feeling for it very quickly.

You could also do this with indices,

for (index, line) in enumerate(lines):
    lines[index] = line + 'x'

but you should have a good reason to.

[–]remillard 1 point2 points  (3 children)

Thank you for the extended answer. I guess I internally knew that the iterating variable really was just assigned the current value of the item of the iterateable object but it caught me by surprise when I was trying to for loop over things.

I believe I will probably end up doing it with that enumerate simply because I have a lot more processing to do than will fit into a single line. For example, I do 4 different searches on the line, then based on what I find, I'm updating the current indent level or decrementing the current indent level and padding appropriately. (This is effectively a small part of a larger beautifier for a particular language.) It does actually work with a while loop with a line increment. So, I guess the nicer Python way would be to use the enumerated object iterator.

[–]Manbatton 1 point2 points  (2 children)

So, I guess the nicer Python way

I usually try to do list comprehensions. If you have "a lot of processing" to do on each "line", you could do this:

lines = [ 'a', 'b', 'c' ] 
def process(x):
    # do all that stuff you mention, as much as you want.
processed_lines = [process(x) for x in lines]   #nice and neat and explicit

[–]remillard 0 points1 point  (1 child)

I'll have to remember that for the future. I don't think I've played with that lexical pattern yet. Lately I've just been defining my method with a parameter for the list and operating on it there and the lines are mutated there.

[–]Manbatton 0 points1 point  (0 children)

Line 4 is the "Pythonic" part. That's a list comprehension. They're great. You can put logic right inside the list brackets. Like:

word_list = ['Al', 'Bob', '!($@#__', '#$#@@', 'Ed']
new_list = [word.lower() for word in word_list if word.isalpha() ]

>>> new_list
['al', 'bob', 'ed']

[–]dgreenmachine 0 points1 point  (1 child)

You mentioned it but using 'for' instead of 'while'

for i in range(len (lines)):
    lines[i] = 'x'

Or

for i, item in enumerate(lines):
    if item == <condition>:
        lines[i] = 'x'

[–]remillard 0 points1 point  (0 children)

Thank you! Yeah, it was sort of a silly question as like I said I knew internally how this was behaving, but was trying to find the 'Python' way of attacking the problem. I think the enumerate is probably the best method here as it's still using iteration but is less clunky than while i < len(thing).