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?

0

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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 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 docs.python.org/3.5/library/stdtypes.html#truth-value-testing
1

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

Comments

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)

Comments

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.