在Python中进行懒惰评估

时间:2013-12-12 04:55:22

标签: python python-3.x lazy-evaluation

Python中什么是延迟评估?

一个网站说:

在Python 3.x中,range()函数返回一个特殊的范围对象,它根据需要计算列表的元素(延迟或延迟评估):

>>> r = range(10)
>>> print(r)
range(0, 10)
>>> print(r[3])
3

这是什么意思?

4 个答案:

答案 0 :(得分:60)

range()(或Python2.x中的xrange())返回的对象称为generator

生成器不是将整个范围[0,1,2,..,9]存储在内存中,而是存储(i=0; i<10; i+=1)的定义,并仅在需要时计算下一个值(AKA延迟评估)。

基本上,生成器允许您返回类似结构的列表,但这里有一些区别:

  1. 列表在创建时存储所有元素。生成器在需要时生成下一个元素。
  2. 列表可以根据需要进行迭代,生成器只能在完全上迭代一次。
  3. 列表可以按索引获取元素,生成器不能 - 它只从头到尾生成一次值。
  4. 可以通过两种方式创建生成器:

    (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 patternswikipedia的github回购告诉我们什么是懒惰评估。

  

延迟expr的eval直到需要它的值并避免重复的evals。

python3中的

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 3惰性变量求值

对于希望使用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?