2

Assume I have a function

def multiply_by(x, multiplier):
    return x * multiplier

How can I create a copy of that function and fix the multiplier in that function?

multiply_by_5 = multiply_by?    <-- here I need python magic

such that multiply_by_5 would have only one argument x and the multiplier would be 5? So that

multiply_by_5(2)
10

Is there a way in Python 2.7 to do that?

niemmi
  • 17,113
  • 7
  • 35
  • 42
chrise
  • 4,039
  • 3
  • 39
  • 74
  • 2
    What is your expected advantage of using multiply_by_5(x) over multiply_by(x, 5)? – ACEG Aug 05 '16 at 12:08
  • Sure, use a [partial](https://docs.python.org/2/library/functools.html#functools.partial). Or create a [closure](http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python) – PM 2Ring Aug 05 '16 at 12:14
  • It isn't called copying, since nothing is actually copied. It's called "partial application". A related notion is that of "curried function". – Elazar Aug 05 '16 at 12:14

5 Answers5

3

You can use functools.partial with keyword argument:

>>> def multiply_by(x, multiplier):
...     return x * multiplier
...
>>> from functools import partial
>>> multiply_by_5 = partial(multiply_by, multiplier=5)
>>> multiply_by_5(2)
10
niemmi
  • 17,113
  • 7
  • 35
  • 42
  • is there any advantage over just defining another function, eventually inline with a `lambda` ? – norok2 Aug 05 '16 at 12:16
  • 1
    @norok2: Not much difference here but please see http://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary for detailed answer – niemmi Aug 05 '16 at 12:24
  • performance difference did not seem relevant in this case, I am not sure about more complex cases, though. As far as elegance, I would not see a major advantage here, but I recognize that it is not always the case.. but thanks @niemmi for pointing out to that interesting discussion. – norok2 Aug 05 '16 at 12:42
  • 4
    It's not only elegance. It's more about using standard, familiar and predictable tools instead of some more arbitrary code that needs to be read and comprehended. – Elazar Aug 05 '16 at 12:54
  • Thanks. Was not aware of that module. nice to know – chrise Aug 05 '16 at 13:50
2

functools.partial is made exactly for this.

you can use it like

import functools
multiply_by_5=functools.partial(multiply_by,multiplier=5)
janbrohl
  • 2,626
  • 1
  • 17
  • 15
2

As suggested by @niemmi's answer, functools.partial is probably the way to go.

However, similar work can be done using curried functions:

def multiply_by(multiplier):
    def multiply(x):
        return multiplier * x
    return multiply

>>> multiply_by_5 = multiply_by(5)    # no magic
>>> multiply_by_5(2)
10

Or using the lambda syntax:

def multiply_by(multiplier):
    return lambda x: multiplier * x

Note that partial is more succinct, more efficient, and more directly express your intent in a standard way. The above technique is an example of the concept called closure, which is means that a function defined in inner scope may refer to variables defined in enclosing scopes, and "close" over them, remembering them, and even mutating them.

Since this technique is more general, it might take the reader of your code more time to understand what exactly do you mean in your code, since your code may be arbitrarily complicated.


Specifically for multiplication (and other operators) partial can be combined with operator.mul:

>>> import functools, operator
>>> multiply_by_5 = functools.partial(operator.mul, 5)
>>> multiply_by_5(2)
10
Community
  • 1
  • 1
Elazar
  • 20,415
  • 4
  • 46
  • 67
  • Thanks. Nice example. Wasn't aware one could do that. Something learned about python. that already makes a good day;) – chrise Aug 05 '16 at 13:52
2

Here's an alternative that doesn't use functools.partial. Instead we define a function inside a function. The inner function "remembers" any of the local variables of the outer function that it needs (including the outer function's arguments). The magic that makes this happen is called closure.

def multiply_factory(multiplier):
    def fixed_multiply(x):
        return x * multiplier
    return fixed_multiply

multiply_by_3 = multiply_factory(3)
multiply_by_5 = multiply_factory(5)

for i in range(5):
    print(i, multiply_by_3(i), multiply_by_5(i))

output

0 0 0
1 3 5
2 6 10
3 9 15
4 12 20

If you want, you can use your existing multiply_by function in the closure, although that's slightly less efficient, due to the overhead of an extra function call. Eg:

def multiply_factory(multiplier):
    def fixed_multiply(x):
        return multiply_by(x, multiplier)
    return fixed_multiply

That can be written more compactly using lambda syntax:

def multiply_factory(multiplier):
    return lambda x: multiply_by(x, multiplier)
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
0

If you cannot change the multiply_by() function, the simplest and perhaps best way is probably

def multiply_by_5(x):
    return multiply_by(x, 5)

You can also use lambda if you really want a one-liner.

However, you may want to change your first function to

def multiply_by(x, multiplier = 5):
    return x * multiplier

Then you can do either of these:

print(multiply_by(4, 3))
12

print(multiply_by(2))
10
Morgan Thrapp
  • 9,748
  • 3
  • 46
  • 67
Rory Daulton
  • 21,934
  • 6
  • 42
  • 50