Reference value of expression inside list comprehension in Python? - python

If I have a list comprehension like
[mymap.get(x, None) for x in oldlist if mymap.get(x,None)]
Is there a way to do mymap.get(x,None) only once here?
I imagine something like
[y for x in oldlist if mymap.get(x,None) as y]
but currently this is a SyntaxError in py 2.x. I would like to be able to reference the resulting values of either the "expression" of list comprehension or from the "list_if" part of it.
I've also tried
[_ for x in oldlist if mymap.get(x,None)]
but that's a NameError, I guess _ being only some interpreter feature on lines.
edit
Is there a way to reference this temporary/anonymous/unnamed variable somehow, without re-iterating the list?

[y for y in (mymap.get(x, None) for x in oldlist) if y]

filter(None, (mymap.get(x, None) for x in oldlist))
or if it suits your fancy the equivalent code using bool instead
filter(bool, (mymap.get(x, None) for x in oldlist))
And to answer your question, there's no way to reference the output function from the predicate.

Related

Having problems with if and else in list comprehensions. I need to make the list value something if the list is empty

I have this:
name_list = [x for x in list]
if not name_list:
name_list = "n/a"
I want to make it in one line.
I want this:
name_list = ["n/a" if not x else x.text for x in list]
The problem I'm having if that I don't know what to put in the first x of the above code.
How do I check if the list is empty and make the value "n/a" in one line?
In the first code snippet, if the list is empty you assign a string to it. Not a list. Hence you can't do it with list comprehension, as the latter always returns a list.
You can use other Python builtin feature to achieve that:
name_list = [x.text for x in list] or "n\a"
It will evaluate the first expression first, and if its Falsey (None, False, 0) it will use the other expression - n\a.
You're mistaken here. You don't want a list comprehension. You use list comprehensions when you need to build new lists. That's not what you want to do here. You want to assign a certain value to name_list depending on a condition.
This can be done using Python's or operator. The or operator is short-circuited, and returns the first 'truthy' value it evaluates:
name_list = [x for x in list] or "n\a"
However, I would recommended being careful using the above code, as it can be confusing for people who aren't familiar with it. Depending on your case, it might be better to be more explicit and break the above code into two steps:
tmp = [x for x in list]
name_list = tmp if tmp else "n\a"
You can simply use or to do that like:
name_list = [x for x in a_list] or "n/a"
or is a short-circuit operator, so it only evaluates the second argument if the first one is false.

can this be done in one line in python?

I feel this can be done in one line, but I cannot find a way to do.
# final_list is what I want as an output
final_list = []
for x in some_list:
# y is a dictionary
y = x.get_some_dict()
# Want to add a new key/value pair to y, which comes from x
y.update({"new_key": x.property_in_x})
# append y to the output list
final_list.append(y)
return final_list
I wouldn't recommend collapsing this into a one line list comprehension. It can be done, but it's bad style. List comprehensions shouldn't have side effects (i.e. calling update).
You could get replace the explicit list appending with a generator. That wouldn't be a bad idea. And d[k] = v is simpler than d.update({k: v}).
def final_list(some_list):
for x in some_list:
y = x.get_some_dict()
y["new_key"] = x.property_in_x
yield y
I wouldn't recommend damping it into a one liner, which will (probably) be messy and unreadable. Also, the update makes a problem with the one liner solution.
But, In my opinion, this can be simplified, to be clearer:
(Shortened, but not to an unreadable one liner)
for x in some_list:
x.get_some_dict().update({"new_key": x.property_in_x})
final_list = [y.get_some_dict() for y in some_list]
Below is the equivalent list comprehension expression along with for loop as:
final_list = [x.get_some_dict() for x in some_list]
for dict_item, base_item in zip(final_list, some_list):
dict_item["new_key"] = base_item.property_in_x

How to set local variable in list comprehension?

I have a method that takes a list and returns an object:
# input a list, returns an object
def map_to_obj(lst):
a_list = f(lst)
return a_list[0] if a_list else None
I want to get a list that contains all the mapped elements that aren't None.
Like this:
v_list = [v1, v2, v3, v4]
[map_to_obj(v) for v in v_list if map_to_obj(v)]
But it doesn't seem good to call the map_to_obj method twice in the list comprehension.
Is there a way to have local variables in list comprehensions so that it can have better performance?
Or does the compiler optimize it automatically?
Here is what I want:
(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end]
Starting in Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), it's possible to use a local variable within a list comprehension in order to avoid calling the same function twice.
In our case, we can name the evaluation of map_to_obj(v) as a variable o while using the result of the expression to filter the list; and thus use o as the mapped value:
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]
Use nested list comprehension:
[x for x in [map_to_obj(v) for v in v_list] if x]
or better still, a list comprehension around a generator expression:
[x for x in (map_to_obj(v) for v in v_list) if x]
A variable assignment is just a singular binding:
[x for v in l for x in [v]]
This is a more general answer and also closer to what you proposed.
So for your problem you can write:
[x for v in v_list for x in [map_to_obj(v)] if x]
You can avoid re-calculation by using python built-in filter:
list(filter(lambda t: t is not None, map(map_to_obj, v_list)))
A local variable can be set within a comprehension by cheating a bit and using an extra 'for' which "iterates" through a 1-element tuple containing the desired value for the local variable. Here's a solution to the OP's problem using this approach:
[o for v in v_list for o in (map_to_obj(v),) if o]
Here, o is the local variable being set equal to map_to_obj(v) for each v.
In my tests this is slightly faster than Lying Dog's nested generator expression (and also faster than the OP's double-call to map_to_obj(v), which, surprisingly, can be faster than the nested generator expression if the map_to_obj function isn't too slow).
List comprehensions are fine for the simple cases, but sometimes a plain old for loop is the simplest solution:
other_list = []
for v in v_list:
obj = map_to_obj(v)
if obj:
other_list.append(obj)
Now if you really want a list comp and dont want to build an tmp list, you can use the iterator versions of filter and map:
import itertools as it
result = list(it.ifilter(None, it.imap(map_to_obj, v_list)))
or more simply :
import itertools as it
result = filter(None, it.imap(map_to_obj, v_list)))
The iterator versions don't build a temporary list, they use lazy evaluation.
I have figured out a way of using reduce:
def map_and_append(lst, v):
mapped = map_to_obj(v)
if mapped is not None:
lst.append(mapped)
return lst
reduce(map_and_append, v_list, [])
How about the performance of this?
The best way I have found to do this, which is cryptic but succinct is like this:
[f(y) for x in some_list()
for y in [some_item(x)] # wastefully making a singleton list
if some_condition(y)]

Python: What's the "Pythonic" way to process two lists?

Say I have this code in Python. I'm a Perl programmer, as you may be able to tell.
# Both list1 and list2 are a list of strings
for x in list1:
for y in list2:
if y in x:
return True
return False
What's a more Pythonic way to handle this? I assume a list comprehension could do it well, but I can't get my head around the "process two separate lists" part of this.
To convert two nested loops into a nested comprehension, you just do this:
[<expression> for x in list1 for y in list2]
If you've never thought through how list comprehensions work, the tutorial explains it:
A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it.
In other words, the clauses from left to right in a comprehension match up with statements from top/outside to bottom/inside, and that's all there is to it.
This blog post attempts to put the same idea in yet another way, in case you haven't got it yet.
But here, you don't have an expression, you have a statement.
But you have an expression in there, the y in x part of the statement, and what you want to do is return True if it's every true for any value, which is exactly what any does. So:
return any([y in x for x in list1 for y in list2])
And really, you don't want to build the list here, just iterate over the values, so drop the square brackets to make it a generator expression instead:
return any(y in x for x in list1 for y in list2)
For the simple case of just iterating the cartesian products of multiple iterables, you may want to use itertools.product instead. In this case, I don't think it makes things any simpler or more readable, but if you had four lists instead of two—or an unpredictable-in-advance number of them—that might be a different story:
return any(y in x for x, y in product(list1, list2))
No, a list comprehension can't do it well. You want a boolean result, list comprehensions are for creating lists (and they don't really do early exit). You could use a generator comprehension:
return any(y in x for x, y in itertools.product(list1, list2))
or if you really like using standard libraries for everything (or you think like a functional programmer):
from itertools import starmap, product
from operator import contains
return any(starmap(contains, product(list1, list2))
Steve and abamarts answers are explaining what you asked explicitly, i would try to address what you implied regarding list comprehensions.
A "nested" list comprehension is nothing more then your original nested for loop, but with the twist, that the most inner-block moves up to the top!
for x in list1:
for y in list2:
if y in x:
return True
else:
return False
becomes:
[True if y in x else False for x in list1 for y in list2]
So the for-loops remain more or less in order:
[for x in list1 for y in list2]
then prepend the if-clause:
[if y in x else False for x in list1 for y in list2]
and finally prepend the result for the if-statement to be True:
[True if y in x else False for x in list1 for y in list2]
a nested example:
tpl_list = []
for element in vector:
for x, y in element:
if (x**2+y**2) < 1:
tpl_list.append((1/x, 1/y))
else:
tpl_list.append((x,y))
as a list comprehension (building in steps)
[for e in vector for x,y in e]
[if (x**2+y**2) < 1 else for e in vector for x,y in e]
[(1/x, 1/y) if (x**2+y**2) < 1 else (x,y) for e in vector for x,y in e]

Mapping a nested list with List Comprehension in Python? [duplicate]

This question already has answers here:
How can I use list comprehensions to process a nested list?
(13 answers)
Closed last month.
I have the following code which I use to map a nested list in Python to produce a list with the same structure.
>>> nested_list = [['Hello', 'World'], ['Goodbye', 'World']]
>>> [map(str.upper, x) for x in nested_list]
[['HELLO', 'WORLD'], ['GOODBYE', 'WORLD']]
Can this be done with list comprehension alone (without using the map function)?
For nested lists you can use nested list comprehensions:
nested_list = [[s.upper() for s in xs] for xs in nested_list]
Personally I find map to be cleaner in this situation, even though I almost always prefer list comprehensions. So it's really your call, since either will work.
Remember the Zen of Python:
There is generally more than one -- and probably several -- obvious ways to do it.**
** Note: Edited for accuracy.
Anyway, I prefer map.
from functools import partial
nested_list = map( partial(map, str.upper), nested_list )
Map is certainly a much cleaner way of doing what you want. You can nest the list comprehensions though, maybe that's what you're after?
[[ix.upper() for ix in x] for x in nested_list]
Other posters have given the answer, but whenever I'm having trouble wrapping my head around a functional construct, I swallow my pride and spell it out longhand with explicitly non-optimal methods and/or objects. You said you wanted to end up with a generator, so:
for xs in n_l:
def doUpper(l):
for x in l:
yield x.upper()
yield doUpper(xs)
for xs in n_l:
yield (x.upper() for x in xs)
((x.upper() for x in xs) for xs in n_l)
Sometimes it's cleaner to keep one of the longhand versions. For me, map and reduce sometimes make it more obvious, but Python idioms might be more obvious for others.
Here is solution for nested list that has arbitrary depth:
def map_nlist(nlist=nlist,fun=lambda x: x*2):
new_list=[]
for i in range(len(nlist)):
if isinstance(nlist[i],list):
new_list += [map_nlist(nlist[i],fun)]
else:
new_list += [fun(nlist[i])]
return new_list
you want to upper case all you list element, just type
In [26]: nested_list = [['Hello', 'World'], ['Goodbye', [['World']]]]
In [27]: map_nlist(nested_list,fun=str.upper)
Out[27]: [['HELLO', 'WORLD'], ['GOODBYE', [['WORLD']]]]
And more important, this recursive function can do more than this!
I am new to python, feel free to discuss!

Categories

Resources