Python方法,仅在第一次调用时计算结果,不需要额外的参数

时间:2016-02-14 01:23:42

标签: python python-3.x

是否有一种惯用的方法来实现"一次方法&​​#34;,即:一种方法,其返回值仅在第一次调用时得到评估?类似于以下简化代码对x的返回值所做的事情:

class X:
    def __init__(self):
        self._x = None

    def x(self):
        if not self._x:
            self._x = some_expensive_call()
        return self._x

4 个答案:

答案 0 :(得分:7)

一种方法是给函数本身一个属性,在第一次调用时只有True

>>> def once():
...     if once.first_call:
...         print('doing some heavy computation over here...')
...         once.result = 1 + 1
...         once.first_call = False
...     return once.result
... 
>>> once.first_call = True
>>> once()
doing some heavy computation over here...
2
>>> once()
2

另一种选择是(ab)使用可变的默认参数。优点是您在定义函数后不必设置属性:

>>> def once(state={'first_call':True}):
...     if state['first_call']:
...         print('doing some heavy computation over here...')
...         state['result'] = 1 + 1
...         state['first_call'] = False
...     return state['result']
... 
>>> once()
doing some heavy computation over here...
2
>>> once()
2

修改

为了完整性,如果您的实例属性只应计算一次,请使用属性:

class Foo(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        if self._x is None:
            self._x = self._compute_x()
        return self._x

    def _compute_x(self):
        print('doing some heavy computation over here...')
        return 1 + 1

演示:

>>> f = Foo()
>>> f.x
doing some heavy computation over here...
2
>>> f.x
2

答案 1 :(得分:6)

从Python 3.2开始,你可以使用functools.lru_cache装饰器(但它可能对你需要的东西有点过分了):

import functools

@functools.lru_cache(maxsize=1)
def once():
    print("calculating expensive result")
    return "expensive result"

once()
once()

输出:

calculating expensive result         # <- only prints on first call
'expensive result'                   # returned value on first call
'expensive result'                   # <- just return value on second call

或者,您可以编写自己的装饰者:

def cache_result(func):
    def wrapper(*args, **kwds):
        if not wrapper.cached:
            wrapper.value = func(*args, **kwds)
            wrapper.cached = True

        return wrapper.value

    wrapper.cached = False

    return functools.update_wrapper(wrapper, func)

并在任何只想运行一次的函数上使用它并缓存结果

@cache_result
def do_once():
    print('doing it once')
    return 'expensive result'

答案 2 :(得分:1)

由于您将此标记为Python 3.X,因此您还可以使用function annotation作为状态标记:

>>> def f()->{"state":False}:
...    if f.__annotations__['return']['state']==False:
...       f.__annotations__['return']['state']=True
... 
>>> f.__annotations__
{'return': {'state': False}}
>>> f()
>>> f.__annotations__
{'return': {'state': True}}

对于课程,timgeb method效果很好。

答案 3 :(得分:1)

老派的方法就是拥有一个保持结果的全局。

_foo_result = None

def foo():
    global _foo_result
    if _foo_result is None:
        _foo_result = _get_me_some_foo()
    return _foo_result

如果None是有效结果,请定义一个空类并改为使用

class _FooNone: pass