2013-12-12 4 views
26

что такое ленивая оценка в python?Lazy оценка python

один сайт сказал:

В Python 3.x функция диапазона() возвращает специальный объект диапазона, который вычисляет элементы списка по требованию (ленивая или отложенной оценка):

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

, что Под этим подразумевается?

+1

Создать генератор (например 'def', содержащий' yield'), который вызывает побочный эффект, такой как 'print', прежде чем он даст значение. Затем запустите генератор с односекундной задержкой на каждую итерацию. Когда происходят отпечатки? Python 3 'range' (так же, как' xrange' в Python 2) работает примерно так: вычисления не выполняются до тех пор, пока не будут заданы. Это то, что означает «ленивая оценка». – user2864740

ответ

42

Объект, возвращенный range() (или xrange() в Python2.x) известен как generator.

Вместо хранения всего диапазона [0,1,2,..,9] в памяти генератор хранит определение для (i=0; i<10; i+=1) и вычисляет следующее значение только тогда, когда это необходимо (AKA ленивая оценка).

По существу, генератор позволяет возвращать список как структура, но вот некоторые отличия:

  1. Перечень хранит все элементы, при его создании. Генератор генерирует следующий элемент, когда это необходимо.
  2. Список может быть переработан как можно больше, генератор может быть повторен только по точно один раз.
  3. Список может получать элементы по индексу, генератор не может - он только генерирует значения один раз, от начала до конца.

Генератор может быть создан двумя способами:

(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 будет вычисляться при создании этого списка.

+0

Это был хороший подробный ответ, bcorso. :) +1 – zx81

+3

Собственно, 'range' (или' xrange' в 2.x) делает * not * возвращает генератор. Генератор - это итератор - для любого генератора 'g' вы можете называть' next (g) '. Объект 'range' на самом деле является итерируемым. Вы можете называть 'iter' на нем, чтобы получить итератор, но он не является итератором (вы не можете называть его' next'). Помимо всего прочего, это означает, что вы можете многократно перебирать один объект диапазона. –

7

Вкратце, ленивая оценка означает, что объект оценивается, когда это необходимо, а не когда оно создается.

В Python 2, диапазон будет возвращать список - это означает, что если вы даете ему большое количество, он будет рассчитывать диапазон и вернуться в момент создания:

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

В Python 3, однако вы получаете специальный объект диапазона:

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

только тогда, когда вы потребляете его, будет ли он на самом деле быть оценены - другими словами, он будет возвращать только цифры в диапазоне, когда вы на самом деле необходимо.

+0

Благодарим вас за определение «ленивый» :) – BoltzmannBrain

0

Реестр github с именем python patterns и wikipedia расскажите нам, что ленивая оценка.

Задерживает вычисление expr до тех пор, пока его значение не понадобится и избежит повторных оценок.

range в python3 не является полной ленивой оценкой, потому что она не избегает повторения 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) 

Чтобы использовать правильное слово, объект питона генератора как диапазон больше похож разработано через call_by_need шаблона, а не ленивы оценки

Смежные вопросы