-1

There is my problem:

Suppose we have 3 functions : f, g, h and the following code

y = f(x)
a = g(y)
b = h(y)

I want to do this on a single line, like :

a,b = g(f(x)),h(f(x))

but this is not efficient if f is very slow ( and doesn't cache it's result)

I have one solution with a generator:

a,b = ((g(y),h(y)) for y in (f(x),)).next()

but this not very readable

I would like to do some thing like that :

with  f(x) as y: a,b = g(y),h(y)

Does anyone have an idea?

( this is cheat

y = f(x);a = g(y);b = h(y)

)

code

import time
def f(t): 
    time.sleep(1)
    print 'f called'
    return t

def g(t): return 1

def h(t): return 2

a,b = g(f(x)),h(f(x))
a,b = ((g(y),h(y)) for y in (f(x),)).next()
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621

3 Answers3

6

Use a lambda. Ta-dah!:

>>> def f(a):
...     return a+1
... 
>>> def g(a):
...     return a*2
... 
>>> def h(a):
...     return a*3
... 
>>> (lambda x: (g(x),h(x)))(1)
(2, 3)
>>> (lambda x: (g(x),h(x)))(f(1))
(4, 6)
>>> a,b=(lambda x: (g(x),h(x)))(f(1))
>>> a
4
>>> b
6
opqdonut
  • 5,119
  • 22
  • 25
  • 2
    imho, this is neither shorter nor more readable than good old `y = f(x); a, b = g(y), h(y)` "cheat" – joaquin Jan 04 '12 at 10:04
  • yeah and an interesting one, that's why I did not donwvoted it. Mine was just a comment (about the fact that often the more idiomatic way is also the simplest one) – joaquin Jan 04 '12 at 11:50
  • By the way, the equivalence between variables and function arguments is a classic pattern that shouws up in many places. (And is even useful in practice if you have to do Javascript asynchronous programming) – hugomg Jan 05 '12 at 20:11
3

I'm probably missing the point here, but I see nothing wrong with

y = f(x); a,b = (g(y), h(y))

If you're doing this operation often enough in your code, and simplicity is what you're after, then perhaps you can create a utility function that maps an argument to a list of functions:

def xmap(v, f_iter):
    "Subjects v to every function in f_iter and returns a list of results"
    return [f(v) for f in f_iter]

You can then do:

a, b = xmap(f(x), [g, h])  

The map idiom is well known so this approach is arguably readable and quite understandable, i.e. xmap() is like map() but with the args and funcs transposed.

Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
  • Your first answer is correct, but this is not functionnal programming. This question is just for fun and for learning python deeply. – user1129519 Jan 04 '12 at 10:00
  • 1
    @user1129519 Fair enough. May I ask why you're trying to do functional programming with Python? If you're learning Python, shouldn't you be focusing on Pythonic idioms rather than that of other paradigms? – Shawn Chin Jan 04 '12 at 10:04
  • Python supports multiple programming paradigms, primarily but not limited to object-oriented, imperative and, to a lesser extent, functional programming styles (wikipaedia) – joaquin Jan 04 '12 at 10:13
  • why do you say `y = f(x); a,b = (g(y), h(y))` is not functional programming? If f, g and h do not have side effects, it fits the paradigm. – Simon Bergot Jan 04 '12 at 10:14
  • @joaquin I agree. My statement was based on the fact that [functional programming isn't one of the strengths of Python](http://stackoverflow.com/questions/1017621/why-isnt-python-very-good-for-functional-programming) and should therefore not be the focus of one looking to learn Python. – Shawn Chin Jan 04 '12 at 10:19
  • @joaquin Python supports functional programming in the sense that it has functions as first class values, allowing higher order programming. This means common functional algorithms with higher order functions can be expressed in Python. It does **not** mean it is easy or desirable write significant Python code in "everything is an expression" style. – Ben Jan 05 '12 at 13:30
  • @Ben you do not have to convince me, I concur. But the OP explicitly said it was just for learning. So it is not bad to focuss on some approaches when learning (like training muscles). Myself, after learning some Haskell, now I feel more confortable using some python tools than I was before. As a result, sometimes, maybe not very often, I realize (get the revelation, tachan!) that `map` (for example) is the tool to use for a given case. – joaquin Jan 05 '12 at 13:56
1

If you want to use the with statement, you can, just decorate f() with contextlib.contextmanager and yield from it:

from contextlib import contextmanager

@contextmanager
def f(t):
    time.sleep(1)
    print 'f called'
    yield t

with f(1) as y:
    a, b = g(y), h(y)
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108