Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most pythonic way to reuse data in multiple calls to same function?

Tags:

python

Normally I would not ask such a question, but python seems to have 1. an unusual level of community consensus on idioms and 2. tends to encourage them by making them more performant (e.g. list comprehension vs map, filter).

This is a pattern I find myself using quite a bit when coding, consider the following JavaScript:

var f = (function() {
  var closedOver = "whatever"
  return function(param) {
    // re-uses closure variable again and again with different param
  }
})();

Or C:

int foo(int x)
{
  /* 
    compile-time constant, will not be recalced for every call,
    name 'someConst' not visible in other scopes 
   */
  const int someConst = 134;
  /* do stuff */
  return whatever;
}

Some possible ways to translate into python:

globalConstant = someConstant
def foo(param):
    # does stuff with param and constant
    return whatever

or possibly:

from functools import partial
def foo(invariant, variant):
    """Actually bar"""
    # does stuff
    return whatever

bar = partial(foo, someInvariant)

or:

class Foo(object):
    """I'm just here to hold a non-visible binding. Actually bar"""
    def __init__(self, invariant):
        super(Foo, self).__init__()
        self.value = invariant

    def __call__(self, param):
        return actualFnResultWithSelfValue

bar = Foo(invariant)

or:

def foo(variant, invariant=someConstantValue):
  return whatever
    

This is unfortunate, now depending on which way I go I may have to use a throw-away name for the initial function definition since I'm only ever using the partially applied version, write a lot of boilerplate classes (which also have throw-away names), or pollute the module namespace with a global constant when its only used in one function, or restrict my function parameters and ensure that someone can break it by calling it with the wrong number of arguments.

I could also 'solve' this by re-instantiating on every call and hoping that it will get optimized away, but since I'm not using pypy I'm not too hopeful on that score.

So my question is two-fold: first, is there a way to do this without the trade-offs? And second, if not, which of the above is the most 'pythonic' (idiomatic, performant, reasonable, etc.)?

like image 674
Jared Smith Avatar asked Nov 23 '16 13:11

Jared Smith


2 Answers

Jared, I totally understand your hesitation to ask this, because it could be answered by many different opinions and spawn a flame war. But, I do agree with your observation: the Python community does tend towards consistency over time with many implementation questions. That's one of the strengths of Python.

Here's my rule of thumb: When in doubt, try to use the Python standard library as much as possible. Your instinct here about functools.partial is correct, for these reasons:

  1. The Python standard library is highly optimized C code that will out-perform any Python class or function closure structure you come up with.
  2. The Python standard library is widely used by other Python programmers, so when you use it, your coding intent will be more widely understood by other Python programmers. ("Code is read more often than it is written.")
  3. The Python standard library is programmed by the core Python developers; no code could possibly claim to be more "Pythonic" than the standard library.

I hope that helps!

like image 67
John Anderson Avatar answered Oct 05 '22 18:10

John Anderson


I'd suggest something that's usually a code smell - default mutable argument.

Trivial example:

def f(x, cache={'x': 0}):
    cache['x'] += x;
    return cache['x']


assert f(1) == 1
assert f(1) == 2  
assert f(1) == 3
assert f(3) == 6

Your dict (or list, or anything which is mutable) is bound to function object. Subsequent calls, when cache keyword argument is omitted, will refer to same object. This object state will persist across calls.

like image 33
Łukasz Rogalski Avatar answered Oct 05 '22 20:10

Łukasz Rogalski