Pythonの遅延評価とは何ですか?
1つのウェブサイトは言った:
Python 3.x range()
関数は、オンデマンドでリストの要素を計算する特別な範囲オブジェクトを返します(遅延評価または遅延評価):
>>> r = range(10)
>>> print(r)
range(0, 10)
>>> print(r[3])
3
これはどういう意味ですか?
range()
(またはPython2.xのxrange()
)によって返されるオブジェクトは、 generator として知られています。
ジェネレータは、_[0,1,2,..,9]
_の範囲全体をメモリに保存する代わりに、_(i=0; i<10; i+=1)
_の定義を保存し、必要な場合にのみ次の値を計算します(レイジー評価)。
基本的に、ジェネレーターを使用すると、構造のようなリストを返すことができますが、いくつかの違いがあります。
ジェネレータは次の2つの方法で作成できます:
(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
を一度に1つずつ生成しますが、このリストが作成されると_x/2
_値のリスト全体が計算されます。
簡単に言えば、遅延評価とは、オブジェクトが作成されたときではなく、必要なときにオブジェクトが評価されることを意味します。
Python 2、範囲はリストを返します-これは、大きな数値を指定すると、範囲を計算して作成時に戻ることを意味します:
>>> i = range(100)
>>> type(i)
<type 'list'>
Python 3、ただし、特別な範囲オブジェクトを取得します:
>>> i = range(100)
>>> type(i)
<class 'range'>
消費した場合のみ、実際に評価されます。つまり、実際に必要な場合にのみ範囲内の数値を返します。
python patterns および wikipedia という名前のgithubリポジトリは、遅延評価とは何かを教えてくれます。
値が必要になるまでexprのevalを遅らせ、evalの繰り返しを回避します。
python3のrange
は、評価の繰り返しを回避しないため、完全な遅延評価ではありません。
遅延評価のより典型的な例は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を遅延評価プロパティに変換するデコレーターです。プロパティに最初にアクセスすると、関数を呼び出して結果を取得し、次にプロパティにアクセスしたときに値が使用されます。
例えば:
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)
適切なWordを使用するには、pythonrangeのようなジェネレーターオブジェクトはcall_by_needパターンではなく、遅延評価