0

I'm having some troubles with removing items from a list. I'm looking for a more elegant solution. Preferably a solution in one for-loop or filter.

The objective of the piece of code: remove all empty entries and all entries starting with a '#' from the config handle.

At the moment i'm using:

# Read the config file and put every line in a seperate entry in a list
configHandle = [item.rstrip('\n') for item in open('config.conf')]

# Strip comment items from the configHandle
for item in configHandle:
    if item.startswith('#'):
        configHandle.remove(item)

# remove all empty items in handle
configHandle = filter(lambda a: a != '', configHandle)
print configHandle

This works but I think it is a bit of a nasty solution.

When I try:

# Read the config file and put every line in a seperate entry in a list
configHandle = [item.rstrip('\n') for item in open('config.conf')]

# Strip comment items and empty items from the configHandle
for item in configHandle:
    if item.startswith('#'):
        configHandle.remove(item)
    elif len(item) == 0:
        configHandle.remove(item)

This, however, fails. I cannot figure out why.

Can someone push me in the right direction?

DCB
  • 107
  • 12

4 Answers4

1

Because You're changing the list while iterating over it. You can use a list comprehension to get ride of this problem:

configHandle = [i for i in configHandle if i and not i.startswith('#')]

Also for opening a file you better to use a with statement that close the file at the end of the block automatically1:

with open('config.conf') as infile :
   configHandle = infile.splitlines()
   configHandle = [line for line in configHandle if line and not line.startswith('#')]

1. Because there is no guarantee for external links to be collected by garbage-collector. And you need to close them explicitly, which can be done by calling the close() method of a file object, or as mentioned as a more pythonic way use a with statement.

Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • Thanks for your reply! I do not understand the first solution (using a comprehension) completely. How does that line else remove the empty entries in the list? Because this also does the trick for me configHandle = [i for i in configHandle if len(i)>0 and not i.startswith('#')] – DCB Aug 05 '16 at 07:14
  • @DCB When you write `if line` python checks the validity of `line` if it evaluate as True python run your condition, and in this case an empty string evaluates as False. Read more here https://docs.python.org/3.5/library/stdtypes.html#truth-value-testing – Mazdak Aug 05 '16 at 10:48
1

Don't remove items while you iterating, it's a common pitfall

Graham
  • 7,431
  • 18
  • 59
  • 84
Ohad Eytan
  • 8,114
  • 1
  • 22
  • 31
0

You aren't allowed to modify an item that you're iterating over.

Instead you should use things like filter or list comprehensions.

configHandle = filter(lambda a: (a != '') and not a.startswith('#'), configHandle)
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
0

Your filter expression is good; just include the additional condition you're looking for:

configHandle = filter(lambda a: a != '' and not a.startswith('#'), configHandle)


There are other options if you don't want to use filter, but, as has been stated in other answers, it is a very bad idea to attempt to modify a list while you are iterating through it. The answers to this stackoverflow question provides alternatives to using filter to remove from a list based on a condition.

Community
  • 1
  • 1
xgord
  • 4,606
  • 6
  • 30
  • 51