Converting list comprehension notation into a for loop - python

I've been seeing many solutions with list comprehensions and i'm wondering if it's possible to convert it into a for loop notation.
For example, if i have a list comprehension notation:
radix = [radix_sort(i,0) for i in lst]
if i write it in:
for i in lst:
radix_sort(i,0)
do i get the same output? What differentiates both? Is it more efficient for list comprehension to be applied rather than conventional for loops?

A list comprehension creates a list—that's the whole point of it. But your loop doesn't create anything at all. So no, you're not going to get the same "output".
The equivalent loop is:
radix = []
for i in lst:
radix.append(radix_sort(i,0))
The list comprehension is defined to mean almost exactly the same thing as this. It may run a bit faster, at least in CPython,* but it will have the same effect.
If that radix_sort returns a copy of the list in sorted order, your loop was doing a lot of work for no effect. But now, as with the list comprehension, you're saving the result of all that work.
If, on the other hand, that radix_sort sorts the list in-place and returns nothing, then both the list comprehension and the explicit loop with append are highly misleading, and you should just use the loop without append.
* For example, in a comprehension, you don't have any way to access radix until the looping is done, so the compiler can make some assumptions and use a faster way of appending to the list.

Related

Converting code to a list comprehension

I wrote a bit of code to iterate over a list to see if a line contained one or more keywords:
STRINGS_TO_MATCH = [ "Foo",
"Bar",
"Oggle" ]
for string in STRINGS_TO_MATCH
if string in line:
val_from_line = line.split(' ')[-1]
Does anyone happen to know if there is a way to make this more readable? Would a list comprehension be a better fit here?
The thing to remember here is that comprehensions are expressions, whose purpose is to create a value - list comprehensions create lists, dict comprehensions create dicts, and set comprehensions create sets. They are unlikely to help in this case, because you aren't creating any such object.
Your code sample is incomplete, because it doesn't do anything with the val_from_line values that it extracts. I am presuming that you want to extract the last "word" from a line which contains any of the strings in STRINGS_TO_MATCH, but it's difficult to work with such incomplete information so this answer might, for all I know, be totally useless.
Assuming I'm correct, the easiest way to find out if line contains any of the STRINGS_TO_MATCH is to use the expression
any(s in line for s in STRINGS_TO_MATCH)
This uses a so-called generator expression, which is similar to a list comprehension - the interpreter can iterate over it to produce a sequence of values - but it doesn't go as far as creating a list of the values, it just creates them as the client code (in this case the any built-in function) requests them. So I might rewrite your code as
if any(s in line for s in STRINGS_TO_MATCH):
val_from_line = line.split(' ')[-1]
I'll leave you to decide what you actually want to do after that, with the warning note that after this code executes val_from_line may or may not exist (depending on whether or not the condition was true), which is never an entirely comfortable situation.

What does the list multiply by one do in a list comprehension below?

Hi I have read some topics in Code Golf and I am having difficulties in understanding the last part of the list comprehension:
lambda l:[i+l.pop()for i in l*1]
why we multiply the list by 1? If I remove the *1 I get one item less in the list.
Multiplying by one gives you a shallow copy of the list, which will be unaffected by the list.pop operations you're performing afterwards, making the behaviour of the comprehension consistent and umambiguous; since you're not popping from the same list over which you're iterating.
They could easily create a shallow copy with the more intuitive l[:] or the more verbose copy.copy(l), but that would take more characters, and the goal of code golf is to use as few characters as possible.

Python: understanding iterators and `join()` better

The join() function accepts an iterable as parameter. However, I was wondering why having:
text = 'asdfqwer'
This:
''.join([c for c in text])
Is significantly faster than:
''.join(c for c in text)
The same occurs with long strings (i.e. text * 10000000).
Watching the memory footprint of both executions with long strings, I think they both create one and only one list of chars in memory, and then join them into a string. So I am guessing perhaps the difference is only between how join() creates this list out of the generator and how the Python interpreter does the same thing when it sees [c for c in text]. But, again, I am just guessing, so I would like somebody to confirm/deny my guesses.
The join method reads its input twice; once to determine how much memory to allocate for the resulting string object, then again to perform the actual join. Passing a list is faster than passing a generator object that it needs to make a copy of so that it can iterate over it twice.
A list comprehension is not simply a generator object wrapped in a list, so constructing the list externally is faster than having join create it from a generator object. Generator objects are optimized for memory efficiency, not speed.
Of course, a string is already an iterable object, so you could just write ''.join(text). (Also again this is not as fast as creating the list explicitly from the string.)

For...in questions (Python)

I was trying some different ways to run some for...in loops. Consider a list of lists:
list_of_lists = []
list = [1, 2, 3, 4, 5]
for i in range(len(list)):
list_of_lists.append(list) # each entry in l_o_l will now be list
Now let's say I want to have the first "column" of l_o_l be included in a separate list, a.
There are several ways I can go about this. For example:
a = [list[0] for list in list_of_lists] # this works (a = [1, 1, 1, 1, 1])
OR
a=[]
for list in list_of_lists:
a.append(hit[0]) #this also works
For the second example, however, I would imagine the "full" expansion to be equivalent to
a=[]
a.append(list[0] for list in list_of_lists) #but this, obviously, produces a generator error
The working "translation" is, in fact,
a=[]
a.append([list[0] for list in list_of_lists]) #this works
My question is on interpretation and punctuation, then. How come Python "knows" to append/does append the list brackets around the "list[0] for list in list_of_lists" expansion (and thus requires it in any rewrite)?
The issue here is that list comprehensions and generator expressions are not just loops, they are more than that.
List comprehensions are designed to be an easy way to build up a list from an iterable, as you have shown.
Your latter two examples both don't work - in both cases you are appending the wrong thing to the list - in the first case, a generator, the second appends a list inside your existing list. Neither of these are what you want.
You are trying to do something in two different ways at the same time, and it doesn't work. Just use the list comprehension - it does what you want to do in the most efficient and readable way.
Your main problem is you seem to have taken list comprehensions and generator expressions and not understood what they are and what they are trying to do. I suggest you try to understand them further before using them.
My question is on interpretation and punctuation, then. How come
Python "knows" to append/does append the list brackets around the
"hit[0] for list in list_of_lists" expansion (and thus requires it in
any rewrite)?
Not sure what that is supposed to mean. I think you might be unaware that in addition to list comprehensions [i*2 for i in range(0,3)] there are also generator expressions (i*2 for i in range(0,3)).
Generator expressions are a syntax for creating generators that perform a mapping, just like a list comprehension (but as a generator). Any list comprehension [c] can be rewritten list(c). The reason why there is a naked c inside list() is because where generator expressions appear as a parameter to a call, it is permitted to drop the brackets. This is what you are seeing in a.append(hit[0] for list in list_of_lists).

Why is python list comprehension sometimes frowned upon?

Many developers I have met suggest it's best practice to go with simple loops and if conditions instead of one line list comprehension statements.
I have always found them very powerful as I can fit a lot of code in a single line and it saves a lot of variables from being created. Why is it still considered a bad practice?
(Is it slow?)
List comprehensions are used for creating lists, for example:
squares = [item ** 2 for item in some_list]
For loops are better for doing something with the elements of a list (or other objects):
for item in some_list:
print(item)
Using a comprehension for its side effects, or a for-loop for creating a list, is generally frowned upon.
Some of the other answers here advocate turning a comprehension into a loop once it becomes too long. I don't think that's good style: the append calls required for creating a list are still ugly. Instead, refactor into a function:
def polynomial(x):
return x ** 4 + 7 * x ** 3 - 2 * x ** 2 + 3 * x - 4
result = [polynomial(x) for x in some_list]
Only if you're concerned about speed – and you've done your profiling! – you should keep the long, unreadable list comprehension.
It's not considered bad.
However, list comprehensions that either
Have side effects, or
Are more readable as a multi-line for loop
are generally frowned upon.
In other words, readability is important.
As an overly contrived example of the first bad example:
x = range(10)
[x.append(5) for _ in xrange(5)]
Basically, if you're not storing the result, and/or it modifies some other object, then a list comprehension is probably a bad idea.
As an example of the second, have a look at pretty much any code golf entry written in python. Once your list comprehension starts to become something that isn't readable at a glance, consider using a for loop.
There is no performance hit (even if there were, I would not worry about it; if performance were so important, you've got the wrong language). Some frown upon list comprehensions because they're a bit too terse for some, that's a matter of personal preference. Generally, I find simple list comprehensions to be much more readable than their loop/condition equivalents, but when they start to get long (e.g. you start to go past 80 characters), they might be better replaced with a loop.

Categories