Using print of else statement in list comprehension

Question:

I want to convert the following for loop to a list comprehension:

  for f in src_files.copy():  # Iterate over a list of images
    if os.path.getsize(os.path.join(SOURCE_DIR, f)) == 0:  # Check if filesize is 0
      print("filename is zero length, so removing.")
      src_files.remove(f)  # Remove corrupted files from list

I had a look a various different other list comprehension questions and came up with this:

src_files = [f for f in src_files if os.path.getsize(os.path.join(SOURCE_DIR, f)) > 0]

So this is working fine but it does not include the print of the else statement. Apparently you have to change the position of the condition if you use an else statement for some reason. So I tried this:

src_files = [f if os.path.getsize(os.path.join(SOURCE_DIR, f)) > 0 else print("filename is zero length, so removing.") for f in src_files]

This does not give me an error but it simply does nothing. The list remains the same as before. I don’t get it. Why does the if condition stop working just because I added a print in the else statement?

(The reason why I want to rewrite the existing code is because I have an enourmous amount of images – hundreds of gigabytes – that need to be processed and I want to make the code as fast as possible)

Asked By: Daniel

||

Answers:

Conceptually, list comprehensions always return a list.

To print the results, you have to scroll down the list to the end.
With the set difference you can get the list of discarded ones.

If you want fast and elegant code, write it this way:

good_list = [f for f in src_files if os.path.getsize(os.path.join(SOURCE_DIR, f)) > 0]

print(*(f"Filename of {x} is zero length, so removing." for x in (set(src_files ) - set(good_list))), sep='n')

However, my advice is to change the approach for writing this algorithm, thinking about lazyness logics for dataloading if there are many images.

Answered By: Giuseppe La Gualano

You’re using a ternary conditional operator (a one-line if-else statement, not to be confused with if-statements at the end of a list comprehension), but this technique in python uses the returned value of an expression to return this value based on your conditions. print is a procedure, meaning it doesn’t return anything meaningful (I’m guessing it returns None or an id).

Ternary conditional operators are optimised to avoid running unnecessary code which isn’t involved in the returned value from the expressions within (they use lazy evaluation). The same goes with lambda functions, if-statements within list comprehension and most other one-line techniques.

That being said, you could put the whole expression inside a print statement, but that wouldn’t let you store to a variable, making your idea impossible in python (as far as I know).

You could use print like so in one line without storing to a variable:

print("n".join(["filename is zero length, so removing." for f in src_files if os.path.getsize(os.path.join(SOURCE_DIR, f)) < 0]))

Or make it a 2-liner:

src_files = [f if os.path.getsize(os.path.join(SOURCE_DIR, f)) > 0 else None for f in src_files]
print("n".join(["filename is zero length, so removing." for i in src_files if i is None]))

This, however, is probably slightly less optimised than the original code without list comprehension, so its probably best just to stick with that.

Answered By: user17301834

It is not clear from your question why you want to print inside the comprehension. However the correct approach for you question, is as follow.
You have a list which you want to filter some element from that based on some check.
then you can use filter:

list(filter(lambda f: os.path.getsize(os.path.join(SOURCE_DIR, f)) > 0, src_file))

also you could filter your list to get the names which are not valid.

Answered By: amirhm
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.