The most pythonic form to display a series of statements?

This is what annoyed me for a while. I learned Haskell before I learned Python, so I always thought of many computations as comparing to a list. It expresses nicely in list comprehension (I give the pythonic version here):

result = [ f(x) for x in list ]

      

In many cases, however, we want to execute more than one statement in x, for example:

result = [ f(g(h(x))) for x in list ]

      

It quickly becomes awkward and difficult to read.

My normal solution is to expand this back into a for loop:

result = []
for x in list:
  x0 = h(x)
  x1 = g(x0)
  x2 = f(x1)
  result.append(x2)

      

One thing that bothers me is that I don't need to initialize an empty result "result". It's trivial, but it makes me unhappy. I was wondering if there are alternative equivalent forms. One way might be using a local function (is this what they call in Python?)

def operation(x):
  x0 = h(x)
  x1 = g(x0)
  x2 = f(x1)
  return x2
result = [ operation(x) for x in list ]

      

Are there any particular advantages / disadvantages for either of the two forms above? Or maybe a more elegant way?

0


source to share


7 replies


You can easily compose functions in Python.

Here's how to create a new function that is a collection of existing functions.



>>> def comp( a, b ):
    def compose( args ):
        return a( b( args ) )
    return compose

>>> def times2(x): return x*2

>>> def plus1(x): return x+1

>>> comp( times2, plus1 )(32)
66

      

Here is a more complete recipe for the composition of a function . This should make it less awkward.

+5


source


Follow the style that best suits your tastes.
I wouldn't worry about performance; only if you really see some kind of problem can you try switching to a different style.

Here are some other possible suggestions besides your suggestions:

result = [f(
              g(
                h(x)
                )
              )
            for x in list]

      



Use progressive lists:

result = [h(x) for x in list]
result = [g(x) for x in result]
result = [f(x) for x in result]

      

Again, this is just a matter of style and taste. Pick the one that you like the most and stick with it :-)

+3


source


If you do this often, and with several different statements, you could write something like

def seriesoffncs(fncs,x):
    for f in fncs[::-1]:
        x=f(x)
    return x

      

where fncs is a list of functions. so consecutive sequences ((f, g, h), x) will return F (G (H (x))). This way, if you later in your code need to practice h (q (g (f (x))), you just execute the selector sequences ((h, q, g, f), x) instead of creating a new function operations for each combination of functions.

+2


source


If your question is only about the last result, your last answer is the best. It is clear to anyone looking at what you are doing.

I often take any code that starts to get complex and translates it into a function. This mainly serves as a comment for this block of code. (any complex code probably needs to be rewritten anyway, and by inserting it into a function I can go back and work on it later)

def operation(x):
  x0 = h(x)
  x1 = g(x0)
  x2 = f(x1)
  return x2
result = [ operation(x) for x in list]

      

+1


source


Dagw.myopenid.com option :

def chained_apply(*args):
    val = args[-1]
    for f in fncs[:-1:-1]:
        val=f(val)
    return val

      

Instead of serial ((h, q, g, f), x) you can now call:

result = chained_apply(foo, bar, baz, x)

      

+1


source


As far as I know, there is no built-in / native syntax for composition in Python, but you can write your own function to compose stuff without too much trouble.

def compose(*f):
    return f[0] if len(f) == 1 else lambda *args: f[0](compose(*f[1:])(*args))

def f(x): 
    return 'o ' + str(x)

def g(x): 
    return 'hai ' + str(x)

def h(x, y): 
    return 'there ' + str(x) + str(y) + '\n'

action = compose(f, g, h)
print [action("Test ", item) for item in [1, 2, 3]]

      

Of course, regardless of understanding is not required.

print [compose(f, g, h)("Test ", item) for item in [1, 2, 3]]

      

This way of composing will work for any number of functions (well, up to the recursion limit) with any number of parameters for the inner function.

+1


source


There are times when it's better to fall back on a for-loop, yes, but more often I prefer one of these approaches:

Use appropriate line breaks and indentation so that they can be read:

result = [blah(blah(blah(x)))
          for x in list]

      

Or extract (enough) the logic into another function as you mentioned. But not necessarily local; Python programmers prefer flat nested structure if you can see a sane way to factorize functionality.

I also came to Python from the world of functional programming and shared your prejudices.

0


source







All Articles