Python中什么是延迟评估?
一个网站说:
在Python 3.x中,range()
函数返回一个特殊的范围对象,它根据需要计算列表的元素(延迟或延迟评估):
>>> r = range(10)
>>> print(r)
range(0, 10)
>>> print(r[3])
3
这是什么意思?
答案 0 :(得分:60)
range()
(或Python2.x中的xrange()
)返回的对象称为generator。
生成器不是将整个范围[0,1,2,..,9]
存储在内存中,而是存储(i=0; i<10; i+=1)
的定义,并仅在需要时计算下一个值(AKA延迟评估)。
基本上,生成器允许您返回类似结构的列表,但这里有一些区别:
可以通过两种方式创建生成器:
(1)非常类似于列表理解:
# this is a list, create all 5000000 x/2 values immediately, uses []
lis = [x/2 for x in range(5000000)]
# this is a generator, creates each x/2 value only when it is needed, uses ()
gen = (x/2 for x in range(5000000))
(2)作为一个函数,使用yield
返回下一个值:
# this is also a generator, it will run until a yield occurs, and return that result.
# on the next call it picks up where it left off and continues until a yield occurs...
def divby2(n):
num = 0
while num < n:
yield num/2
num += 1
# same as (x/2 for x in range(5000000))
print divby2(5000000)
注意:即使range(5000000)
是Python3.x中的生成器,[x/2 for x in range(5000000)]
仍然是一个列表。 range(...)
完成任务并一次生成一个x
,但创建此列表时将计算整个x/2
值列表。
答案 1 :(得分:12)
简而言之,延迟评估意味着在需要时对象进行评估,而不是在创建对象时进行评估。
在Python 2中,range将返回一个列表 - 这意味着如果你给它一个大数字,它将计算范围并在创建时返回:
>>> i = range(100)
>>> type(i)
<type 'list'>
在Python 3中,您会得到一个特殊的范围对象:
>>> i = range(100)
>>> type(i)
<class 'range'>
只有当你消费它时,它才会被实际评估 - 换句话说,它只会在你真正需要它时返回范围内的数字。
答案 2 :(得分:2)
名为python patterns和wikipedia的github回购告诉我们什么是懒惰评估。
python3中的延迟expr的eval直到需要它的值并避免重复的evals。
range
不是一个完整的延迟评估,因为它不会避免重复的eval。
懒惰评估的一个更经典的例子是cached_property
:
import functools
class cached_property(object):
def __init__(self, function):
self.function = function
functools.update_wrapper(self, function)
def __get__(self, obj, type_):
if obj is None:
return self
val = self.function(obj)
obj.__dict__[self.function.__name__] = val
return val
cached_property(a.k.a lazy_property)是一个装饰器,它将func转换为惰性求值属性。第一次访问属性时,将调用func以获取结果,然后在下次访问该属性时使用该值。
例如:
class LogHandler:
def __init__(self, file_path):
self.file_path = file_path
@cached_property
def load_log_file(self):
with open(self.file_path) as f:
# the file is to big that I have to cost 2s to read all file
return f.read()
log_handler = LogHandler('./sys.log')
# only the first time call will cost 2s.
print(log_handler.load_log_file)
# return value is cached to the log_handler obj.
print(log_handler.load_log_file)
要使用正确的单词,像 range 这样的python生成器对象更像是通过 call_by_need 模式设计的,而不是懒惰的评估
答案 3 :(得分:-1)
使用@functools.lru_cache
对于希望使用Python进行延迟变量评估的Google员工,请查看https://docs.python.org/3.7/library/functools.html#functools.lru_cache
文档示例:
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
另请参阅:Is there a decorator to simply cache function return values?