是否有一种惯用的方法来实现"一次方法",即:一种方法,其返回值仅在第一次调用时得到评估?类似于以下简化代码对x
的返回值所做的事情:
class X:
def __init__(self):
self._x = None
def x(self):
if not self._x:
self._x = some_expensive_call()
return self._x
答案 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