2013-12-12 21 views
26

đánh giá lười biếng trong python là gì?python đánh giá lười biếng

một trang web cho biết:

Trong Python 3.x phạm vi() Hàm trả về một đối tượng phạm vi đặc biệt mà tính các yếu tố của danh sách theo yêu cầu (lười biếng hoặc chậm đánh giá):

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

gì có nghĩa là gì?

+1

Tạo một máy phát điện (ví dụ 'def' chứa' yield') gây ra tác dụng phụ như 'print' trước khi nó mang lại giá trị. Sau đó, lặp máy phát điện với độ trễ một giây mỗi lần lặp. Khi nào các bản in xuất hiện? Python '' phạm vi' (giống như 'xrange' trong Python 2) hoạt động như thế: các tính toán không được thực hiện cho đến khi được yêu cầu. Đây là những gì "đánh giá lười biếng" có nghĩa là. – user2864740

Trả lời

42

Đối tượng được trả về range() (hoặc xrange() trong Python2.x) được gọi là generator.

Thay vì lưu toàn bộ dải, [0,1,2,..,9], trong bộ nhớ, trình tạo lưu trữ định nghĩa cho (i=0; i<10; i+=1) và tính giá trị tiếp theo chỉ khi cần (đánh giá lười biếng AKA).

Về cơ bản, một máy phát điện cho phép bạn trả về một danh sách như cấu trúc, nhưng đây là một số khác biệt:

  1. Một danh sách lưu trữ tất cả các yếu tố khi nó được tạo ra. Một máy phát tạo ra phần tử tiếp theo khi cần.
  2. Danh sách có thể được lặp lại nhiều như bạn cần, chỉ có thể lặp lại máy phát qua chính xác một lần.
  3. Danh sách có thể lấy các phần tử theo chỉ mục, một trình tạo không thể - nó chỉ tạo ra các giá trị một lần, từ đầu đến cuối.

Một máy phát điện có thể được tạo ra theo hai cách:

(1) Rất giống với một danh sách hiểu:

# 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) Là một chức năng, sử dụng yield để trả lại giá trị tiếp theo:

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

Lưu ý: Mặc dù range(5000000) là một trình tạo trong Python3.x, [x/2 for x in range(5000000)] vẫn là một danh sách. range(...) thực hiện công việc và tạo ra x mỗi lần, nhưng toàn bộ danh sách các giá trị x/2 sẽ được tính khi danh sách này được tạo.

+0

Đó là một câu trả lời chi tiết hay, bcorso. :) +1 – zx81

+3

Thực ra, 'dải ô' (hoặc' xrange' trong 2.x) không * không * trả về một trình tạo. Một bộ tạo là một trình lặp - cho bất kỳ trình tạo 'g' nào bạn có thể gọi' next (g) '. Đối tượng 'range' thực sự là một iterable. Bạn có thể gọi 'iter' trên nó để có được một iterator, nhưng nó không phải là một iterator chính nó (bạn không thể gọi' next' trên nó). Trong số những thứ khác, điều này có nghĩa là bạn có thể lặp qua một đối tượng phạm vi duy nhất nhiều lần. –

7

Tóm lại, đánh giá lười biếng có nghĩa là đối tượng được đánh giá khi cần thiết, không phải khi đối tượng được tạo.

Trong Python 2, phạm vi sẽ trả về một danh sách - điều này có nghĩa rằng nếu bạn cung cấp cho nó một số lượng lớn, nó sẽ tính toán phạm vi và trở về vào thời điểm tạo:

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

Trong Python 3, tuy nhiên bạn sẽ có được một đối tượng phạm vi đặc biệt:

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

chỉ khi bạn tiêu thụ nó, nó sẽ thực sự được đánh giá - nói cách khác, nó sẽ chỉ trả lại những con số trong phạm vi khi bạn thực sự cần chúng.

+0

Cảm ơn bạn đã xác định "lười" :) – BoltzmannBrain

0

Repo github có tên python patternswikipedia cho chúng tôi biết đánh giá lười biếng là gì.

Trì hoãn đánh giá của expr cho đến khi giá trị của nó là cần thiết và tránh lặp lại evals.

range trong python3 không phải là một đánh giá hoàn toàn lười biếng, bởi vì nó không tránh lặp lại eval.

Một ví dụ điển hình hơn để đánh giá lười biếng là 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 

Các cached_property (còn được gọi lazy_property) là một trang trí mà chuyển đổi một func thành một tài sản đánh giá lười biếng. Thuộc tính lần đầu tiên được truy cập, hàm func được gọi để nhận kết quả và sau đó giá trị được sử dụng vào lần tiếp theo bạn truy cập thuộc tính.

ví dụ:

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) 

Để sử dụng một từ thích hợp, một đối tượng phát python như phạm vi đều giống như thiết kế thông qua call_by_need mô hình, chứ không phải là lười biếng đánh giá

Các vấn đề liên quan