在多次调用同一函数时重用数据的最pythonic方法是什么?

时间:2016-11-23 13:41:27

标签: python

通常情况下我不会问这样的问题,但是python似乎有1.一个不同寻常的社区对习语的共识,并且倾向于通过使它们更高效来鼓励它们(例如列表理解与地图,过滤器)。 / p>

这是我在编码时发现自己使用的模式,请考虑以下JavaScript:

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

或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;
}

转换为python的一些可能方法:

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

或可能:

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

bar = partial(foo, someInvariant)

或:

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)

或:

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

这很不幸,现在取决于我走哪条路,我可能不得不使用一个丢弃名称作为初始函数定义,因为我只使用部分应用的版本,编写了很多样板类(也是当它只在一个函数中使用时,或者使用全局常量污染模块命名空间,或者限制我的函数参数并确保有人可以通过使用错误数量的参数调用它来破坏它。

我也可以通过重新实例化每一个电话来解决这个问题,并希望它会被优化掉,但是因为我没有使用pypy,所以我不会对这个分数抱太大希望。

所以我的问题有两个方面:首先,有没有办法在没有权衡的情况下做到这一点?第二,如果没有,上面哪个是最“pythonic”(惯用的,高性的,合理的等)?

5 个答案:

答案 0 :(得分:4)

杰瑞德,我完全明白你犹豫要问这个,因为它可以通过许多不同的意见来回答,并产生一场火焰战争。但是,我同意你的看法:随着时间的推移,Python社区确实倾向于与许多实现问题保持一致。这是Python的优势之一。

这是我的经验法则:如有疑问,请尽量使用Python标准库。您对此functools.partial的直觉是正确的,原因如下:

  1. Python标准库是高度优化的C代码,它将超出您提出的任何Python类或函数闭包结构。
  2. Python标准库被其他Python程序员广泛使用,因此当您使用它时,其他Python程序员将更广泛地理解您的编码意图。 (“代码的阅读频率高于编写代码。”)
  3. Python标准库由核心Python开发人员编写;没有代码可能声称比标准库更“Pythonic”。
  4. 我希望有所帮助!

答案 1 :(得分:1)

我建议一些通常是代码味道的东西 - 默认的可变参数。

琐碎的例子:

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

您的dict(或列表,或任何可变的)绑定到函数对象。当省略cache关键字参数时,后续调用将引用同一对象。此对象状态将在调用过程中持续存在。

答案 2 :(得分:0)

我不喜欢外部范围的var的想法,并且可能建议使用闭包,我认为这是更好的方式,因为你看它更像JavaScript,所以你可以使用函数作为对象:

def foo():
    const = 1
    def bar(param):
      return const + param
    return bar

a = foo()
print(a(5))

答案 3 :(得分:0)

有一个你没有提到的替代方案,这是IMO最不会产生扔掉名字的pythonic方式:)

def foo(variant):
    try:
        return foo.invariant + variant
    except AttributeError:
        foo.invariant = 1
        return foo(variant)

虽然坚持使用std库是最好的。

答案 4 :(得分:0)

我不认为你的两个第一个例子是等价的。第一个似乎是闭包,而第二个使用全局常量。无论如何,对于第一个例子,python中的直接等价物将是

def f(x):
   def g(y):
      return x + y
   return g

并像

一样使用它
add2 = f(2)
print(add2(3)) # == 5

实际上,您使用partial实现了类似的内容