Mapping elements using multi-line functions - python

Consider following example in JavaScript:
var x = [1, 2, 3]
var y = x
.map(i => i * 2)
.map(i => i * i)
If I would like to call some function that does some external-state manipulation, like logging or publishing message to a topic I would simply use the brackets to get multi-line lambda:
var x = [1, 2, 3]
var y = x
.map(i => i * 2)
.map(i => {
console.log("arg received ": + i)
return i * i
})
As far as I know, such a trivial thing is impossible to do in Python because of single line lambdas and we have to use named functions which sucks because any code reader or code reviewer have to jump between function definition and function usage, even though the function is used only once.
Is there any industry standard for solving such a trivial issue?

Specifically for logging or other "void" functions, you could use or:
>>> lst = [1, 2, 3]
>>> list(map(lambda x: x*x, map(lambda x: print(x) or x, map(lambda x: x*2, lst))))
2
4
6
[4, 16, 36]
Here, a or b is equivalent to a if a else b, and since print(x) evaluates to None, which is not "truthy", the "else" part is returned after the print is executed.
Also works as a (nested) list comprehension/generator:
>>> [print(x) or x*x for x in (x*2 for x in lst)]
2
4
6
[4, 16, 36]
Whether that's good style is a different question, though.
Alternatively, and probably a bit cleaner, define a log/peek/debug function that logs the value and then returns it, to be put anywhere in your map/list-comp chain:
>>> def peek(x, f=print):
... f(x)
... return x
...
>>> [x*x for x in map(peek, (x*2 for x in lst))]
2
4
6
[4, 16, 36]

Python doesn't really have this concept, the best you can do is just to define an inner function right above where you'd use it
def my_func(i):
return i * i
y = map(my_func, map(lambda i: i * i, x))

Related

Assignments in python list comprehension

I am looking for a way to do assignments in a list comprehension. I would like to rewrite something like the following piece of code into a list comprehension.
I have this "costly" function:
import time
def f(x):
time.sleep(1)
return x + 1
And this loop:
l = []
for value in [1, 2, 3]:
x = f(value)
l.append(x + x)
I would like to rewrite this to a list comprehension:
l = [
f(value) + f(fvalue)
for value in [1, 2, 3]
]
But since calling f(value) is costly, I would like to minimize the number of calls (this following snippet doesn't run):
l = [
(x = f(value))
x + x
for value in [1, 2, 3]
]
I've read about the assignment expression (:=) (https://www.python.org/dev/peps/pep-0572/#changing-the-scope-rules-for-comprehensions) but I can't seem to figure it out.
My approach would be to nest multiple list comprehension, like
l_new = [x * x * y for x, y in [f(value) for value in [1, 2, 3]]]
So f() should only be called once for each value.
Here is another way to fake it:
[x+x for value in [1, 2, 3] for x in [f(value)]]
This can be done with a walrus operator. Note that you need Python 3.8 or later.
l = [x:=10 * value for value in [1, 2, 3]]
With one fast and slow function.
import time
def slow_function(a):
time.sleep(1)
return a + 1
def fast_function(b):
return b + b
l = [
fast_function(x := slow_function(value))
for value in [1, 2, 3]
]
The walrus operator can be skipped in both these examples, but perhaps the real problem would require it.

Python Function Returning List Objects Out of Order

I'm relatively new to the Python language. I'm aware of most of the basic functionality & theory, all the different classes of object and their behavior/properties etc.
Anyway, I was writing basic functions to explore different concepts in practice and get to know the language more intuitively. One in particular has left me vexed! Can anyone share any insight into why this result is not as expected?
Here is the code I ran:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
for n in k:
k[k.index(n)] = n * 2
x, y, z = k
return k
test_func(test_list)
print(test_list)
print(x)
print(y)
print(z)
I would have expected the result to be:
[4, 8, 12]
4
8
12
However, the actual result is as follows:
[8, 4, 12]
8
4
12
It seems that the first two items of the list have been swapped.
I can't see what could be causing this? If anyone can see what's happening here, please share the insight!
Thanks,
Oscar South
After first iteration you list look like [4,4,6] so k.index(4) return 0 index and multiple it by 2. So final result is [8,4,12].
I think you meant to do this:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
for i in range(len(k)):
k[i] = k[i] * 2
x, y, z = k
return k
You're mixing indexes with values, and using index() to find the position in an array is incorrect, most of all because you're modifying the list and you'll find the same elements again but in different positions, better use a range to iterate over the indexes and retrieve the values.
Also, using globals is not cool. In fact, the whole procedure should be written like a list comprehension instead - simple and idiomatic:
[2 * x for x in test_list]
=> [4, 8, 12]
In the first iteration the first element is changed to 4. In the second iteration the index of 4 is 0 not 1 as you expect.
Try this:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
l = list()
for n in k:
print(n, k.index(n))
l.append(n * 2)
x, y, z = l
return l
test_func(test_list)
print(test_list)
print(x)
print(y)
print(z)
You can condense the code into a list comprehension and unpacking:
test_list = [2, 4, 6]
def test_func(k):
global x, y, z
x, y, z = [i*2 for i in k]
return [x, y, z]

Execute a code block in a map in Python

I want to replicate the following JavaScript code in Python:
let a = [0, 4, 5]
b = a.map(x => {
if(x < 3) return 0
else return 1
})
Any idea how I can do this?
I'm not sure how execute a code block in a map function.
You can either make a function, or use a lambda function like this:
>>> a = [0, 4, 5]
>>> b = map(lambda x: 0 if x < 3 else 1, a)
>>> b
[0, 1, 1]
The only kind of anonymous functions in Python are lambdas, and they're limited to being only an expression, if you want a proper function you have to give it a name:
def map_f(x):
if x < 3:
return 0
else:
return 1
b = map(map_f, a)
Personally, I prefer list comprehension to the map function.
>>> a = [0, 4, 5]
>>> [int(x >= 3) for x in a]
[0, 1, 1]
They allow you to use whatever expression you want without having to create a function.

Python Reduce Math Equation

I am given a list of numbers a = [1, 2, 3, 4, 5, 6] and using only Python's reduce function, return a value. The value is calculated by:
(((...(a[0] + a[1]) * a[2] + a[3]) * a[4] + ...)
So in the above example where a = [1, 2, 3, 4, 5, 6], the answer should be ((1 + 2) * 3 + 4) * 5 + 6 = 71
I'm fairly new to Python and I'm trying to learn it:
reduce(lambda x,y: x * y, map(lambda x,y: x+y, numbers))
The tricky bit is, of course, that you have to alternate between two functions in reduce. Since you can't really keep track of the current position in the list1), in order to decide which operation to use, your best chance is to use an iterator that alternates between + and *.
You could use itertools.cycle to alternate between add and mul and use those in reduce
>>> import itertools, operator
>>> op = itertools.cycle([operator.add, operator.mul])
>>> a = [1, 2, 3, 4, 5, 6]
>>> reduce(lambda x, y: next(op)(x, y), a)
71
If you can't use those modules, you can make a generator expression alternating between + and * using lambdas in a ternary expression. The rest remains the same.
>>> op = ((lambda x,y:x+y) if i % 2 == 0 else (lambda x,y:x*y) for i in range(len(a)))
>>> a = [1, 2, 3, 4, 5, 6]
>>> reduce(lambda x, y: next(op)(x, y), a)
71
1) Well, you can, using enumerate(a), or zip(range(len(a)), a), if you prefer...
>>> a = [1, 2, 3, 4, 5, 6]
>>> reduce(lambda x, y: (y[0], x[1]+y[1]) if x[0]%2==0 else (y[0], x[1]*y[1]), enumerate(a))[1]
71
Or shorter, using tuple-unpacking in the lambdas, but this only works in Python 2.x:
>>> reduce(lambda (i,x),(j,y): (j, x+y) if i%2==0 else (j, x*y), enumerate(a))[1]
Improving the solution of Akash Patel, i have come up with this, with only reduce.
reduce(lambda acc,x: (acc+x[1] if x[0]%2 else acc*x[1]), enumerate(numbers), 1)
We need to add odd number of elements and multiply even number of elements with previous elements.
This was a tough problem because you wanted to use everything in reduce. So just use reduce twice!
reduce(lambda x, y: x + y, reduce(lambda x, y: (x[0], x[1] + y[1]) if y[0]%2==1 else (x[0], x[1]*y[1]) , enumerate(a)))

How to add the content of two tuples (or lists)?

I already had this problem multiple times, that I couldn't find a good solution to add the content of two tuples together.
Something that does :
a = (1, 2)
b = (3, 4)
c = (a[0]+b[0], a[1]+b[1])
I think I saw a syntax to do just that once, but I can't remember how to do it.
This one also works:
>>> a = (1,2)
>>> b = (3,4)
>>> c = map(sum, zip(a,b))
>>> c
[4, 6]
It should work with any number of lists containing any number of numbers.
One liner:
map(lambda x, y: x+ y, a, b)
I believe this is the most efficient way. You can also import operator.add to avoid the lambda function. For me I rather prefer cleaner global namespace.
With generator comprehension:
a = (1, 2)
b = (3, 4)
result = [x + y for x, y in zip(a, b)]
[4, 6]

Categories

Resources