joshphysics
joshphysics

Reputation: 211

Is there a better way than this to write Python functions that "depend on parameters"?

Consider the Python function line defined as follows:

def line(m, b):
    def inner_function(x):
        return m * x + b
    return inner_function

This function has the property that for any floats m and b, the object line(m, b) is a Python function, and when line(m, b) is called on a float x, it returns a float line(m, b)(x). The float line(m, b)(x) can be interpreted as the value of the line with slope m and y-intercept b at the point x. This is one method for writing a Python function that "depends on parameters" m and b.

  1. Is there a special name for this method of writing a Python function that depends on some parameters?
  2. Is there a more Pythonic and/or computationally efficient way to write a function that does the same thing as line above?

Upvotes: 3

Views: 269

Answers (3)

ShadowRanger
ShadowRanger

Reputation: 155363

This is called a closure, and it's a perfectly reasonable way to write one, as well as one of the most efficient means of doing so (in the CPython reference interpreter anyway).

The only other common pattern I know of is the equivalent of C++'s functors, where a class has the state as attributes, and the additional parameters are passed to __call__, e.g. to match your case:

class Line:
    def __init__(self, m, b):
        self.m = m
        self.b = b
    def __call__(self, x):
        return self.m * x + self.b

It's used identically, either creating/storing an instance and reusing it, or as in your example, creating it, using it once, and throwing it away (Line(m, b)(x)). Functors are slower than closures though (as attribute access is more expensive than reading from nested scope, at least in the CPython reference interpreter), and as you can see, they're more verbose as well, so I'd generally recommend the closure unless your needs require the greater flexibility/power of class instances.

Upvotes: 6

shx2
shx2

Reputation: 64308

I support @ShaddowRanger's answer. But using partial is another nice approach.

import functools
def f(m, b, x):
    return m * x + b
line = functools.partial(f, 2, 3)
line(5)
=> 13

One thing which is worth pointing out is that lambda objects, and OP's inner_function aren't pickleable, whereas line here, as well as @ShaddowRanger's Line objects are, which makes them a bit more useful.

Upvotes: 3

Scott Hunter
Scott Hunter

Reputation: 49803

This is a little shorter:

def line(m,b):
    return lambda x: m*x+b;

Upvotes: 1

Related Questions