2

I'm doing a for loop over a list in Python, which to all of my knowledge should create a shallow copy of each element in the list. However, when I perform operations on elements, the changes aren't being reflected in the original list.

Here's the code:

interactions = f.readlines()
for interaction in interactions:
    orig = interaction
    interaction = interaction.replace('.', '').replace(' ', '').strip('\n')
    if len(interaction) == 0:
        interactions.remove(orig)

    print(interactions)

The output of this operation given an initial list of ['. Bph\n', 'Cardiovascular\n', 'Diabetes\n', '. '] is:

['. Bph\n', 'Cardiovascular\n', 'Diabetes\n', '. ']
['. Bph\n', 'Cardiovascular\n', 'Diabetes\n', '. ']
['. Bph\n', 'Cardiovascular\n', 'Diabetes\n', '. ']
['. Bph\n', 'Cardiovascular\n', 'Diabetes\n']

Even though when I follow the debugger I can see the correct operations being performed on interaction but the changes just arent being reflected in interactions. What am I doing wrong here?

bendl
  • 1,583
  • 1
  • 18
  • 41
  • Jim answered rather perfectly, but keep in mind that though Python __does__ perform shallow copy on assignment, `str` objects are immutable, hence no `str` method will ever modify the object in place. – Right leg Nov 14 '16 at 01:13

2 Answers2

2

Why should it reflect? You're modifying str elements and they can't get altered in place, new strings containing the altered value get returned; nothing reflects in the original list. :-)

Instead, you should iterate over a copy of interactions[:] and assign using the index enumerate produces:

for i, interaction in enumerate(interactions[:]):
    # redundant in this snippet
    orig = interaction   
    # strip() will remove '\n' and leading-trailing ' '
    interaction = interaction.replace('.', '').replace(' ', '').strip('\n')   
    # if interaction: suffices
    if len(interaction) == 0:  
        interactions.remove(orig)
    else:
        interactions[i] = interaction
    print(interactions)

This now gives output of:

['Bph', 'Cardiovascular\n', 'Diabetes\n', '. ']
['Bph', 'Cardiovascular', 'Diabetes\n', '. ']
['Bph', 'Cardiovascular', 'Diabetes', '. ']
['Bph', 'Cardiovascular', 'Diabetes']
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
1

Python strings are immutable, so you either need to replace each element with your cleaned version (using indices like in Jim's answer) or build up a new list. IMO the neatest way to do this is with a list comprehension:

interactions = [i.replace('.', '').replace(' ', '').strip('\n') for i in interactions]
interactions = list(filter(None, interactions)) # Remove empty elements
John Lyon
  • 11,180
  • 4
  • 36
  • 44
  • Jim gives a slightly better description of where I went wrong, but this is definitely the cleaner solution. – bendl Nov 14 '16 at 01:55