Python 中的惰性求值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20535342/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:41:19  来源:igfitidea点击:

Lazy evaluation in Python

pythonpython-3.xlazy-evaluation

提问by Vipul

What is lazy evaluation in Python?

Python 中的惰性求值是什么?

One website said :

一个网站说:

In Python 3.x the range()function returns a special range object which computes elements of the list on demand (lazy or deferred evaluation):

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

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

What is meant by this?

这是什么意思?

采纳答案by bcorso

The object returned by range()(or xrange()in Python2.x) is known as a lazy iterable.

range()(或xrange()在 Python2.x 中)返回的对象被称为惰性可迭代对象。

Instead of storing the entire range, [0,1,2,..,9], in memory, the generator stores a definition for (i=0; i<10; i+=1)and computes the next value only when needed (AKA lazy-evaluation).

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

Essentially, a generator allows you to return a list like structure, but here are some differences:

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

  1. A list stores all elements when it is created. A generator generates the next element when it is needed.
  2. A list can be iterated over as much as you need, a generator can only be iterated over exactlyonce.
  3. A list can get elements by index, a generator cannot -- it only generates values once, from start to end.
  1. 列表在创建时存储所有元素。生成器在需要时生成下一个元素。
  2. 列表可以遍历尽可能多的,因为你需要,发电机只能遍历正好一次。
  3. 列表可以通过索引获取元素,生成器不能——它从头到尾只生成一次值。

A generator can be created in two ways:

可以通过两种方式创建生成器:

(1) Very similar to a list comprehension:

(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) As a function, using yieldto return the next value:

(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)

Note:Even though range(5000000)is a generator in Python3.x, [x/2 for x in range(5000000)]is still a list. range(...)does it's job and generates xone at a time, but the entire list of x/2values will be computed when this list is create.

注意:尽管range(5000000)是 Python3.x 中的生成器,但[x/2 for x in range(5000000)]仍然是一个列表。range(...)完成它的工作并一次生成x一个,但是在x/2创建此列表时将计算整个值列表。

回答by Burhan Khalid

In a nutshell, lazy evaluation means that the object is evaluated when it is needed, not when it is created.

简而言之,惰性求值意味着对象在需要时进行求值,而不是在创建时求值。

In Python 2, range will return a list - this means that if you give it a large number, it will calculate the range and return at the time of creation:

在 Python 2 中,range 将返回一个列表——这意味着如果你给它一个很大的数字,它会计算范围并在创建时返回:

>>> i = range(100)
>>> type(i)
<type 'list'>

In Python 3, however you get a special range object:

但是,在 Python 3 中,您会得到一个特殊的范围对象:

>>> i = range(100)
>>> type(i)
<class 'range'>

Only when you consume it, will it actually be evaluated - in other words, it will only return the numbers in the range when you actually need them.

只有当你使用它时,它才会真正被评估——换句话说,它只会在你真正需要它们时返回范围内的数字。

回答by Vi.Ci

A github repo named python patternsand wikipediatell us what lazy evaluation is.

一个名为python 模式维基百科的github仓库告诉我们什么是惰性求值。

Delays the eval of an expr until its value is needed and avoids repeated evals.

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

rangein python3 is not a complete lazy evaluation, because it doesn't avoid repeated eval.

range在 python3 中不是一个完整的惰性求值,因为它不能避免重复求值。

A more classic example for lazy evaluation is cached_property:

惰性求值的一个更经典的例子是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

The cached_property(a.k.a lazy_property) is a decorator which convert a func into a lazy evaluation property. The first time property accessed, the func is called to get result and then the value is used the next time you access the property.

cached_property(又名 lazy_property)是一个装饰器,它将 func 转换为惰性求值属性。第一次访问属性时,调用 func 以获取结果,然后在下次访问该属性时使用该值。

eg:

例如:

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)

To use a proper word, a python generator object like rangeare more like designed through call_by_needpattern, rather than lazy evaluation

用一个恰当的词来说,像range这样的 python 生成器对象更像是通过call_by_need模式设计的,而不是惰性求值