はじめに
この実験では、Python の組み込みの反復子、ジェネレータ、およびジェネレータ式について学びます。これらの構文がどのように使われて Python で効率的でエレガントなコードを書けるか見ていきます。
到達目標
- 反復子
- ジェネレータ
- ジェネレータ式
この実験では、Python の組み込みの反復子、ジェネレータ、およびジェネレータ式について学びます。これらの構文がどのように使われて Python で効率的でエレガントなコードを書けるか見ていきます。
反復子は、反復(ループ)可能なオブジェクトです。1 回に 1 要素ずつデータを返すオブジェクトです。Python では、反復子はリスト、タプル、または文字列などの反復可能オブジェクトから作成されます。
新しい Python インタプリタを開きます。
python3
Python で反復子を作成するには、オブジェクトに 2 つのメソッドを実装する必要があります。__iter__
と __next__
です。
__iter__
は反復子オブジェクト自体を返します。__next__
メソッドは反復子から次の値を返します。返す項目がなくなった場合は、StopIteration
をスローする必要があります。
次は、数値のリストを反復する単純な反復子の例です。
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
## len() はリスト内の要素数
if self.index >= len(self.data):
raise StopIteration
result = self.data[self.index]
self.index += 1
return result
iterator = MyIterator([1, 2, 3, 4, 5])
for x in iterator:
print(x)
出力:
1
2
3
4
5
反復子は便利です。なぜなら、反復可能オブジェクトの要素を一度に 1 つずつアクセスできるからです。一度にすべての要素をメモリに読み込むのではなく、これは特にメモリに収まらない大規模なデータセットを扱う際に便利です。
反復子はまた、Python で遅延評価を実装するためにも使用されます。これは、反復子の要素が必要になるまで生成されるだけで、事前にすべての要素を生成するわけではないことを意味します。これは、不要な要素を生成および格納することを回避できるため、より効率的なアプローチになる場合があります。
反復子から 1 回に 1 要素を取得したい場合は、next()
関数を使用できます。この関数は反復子から次の要素を返します。要素がなくなった場合は、StopIteration
例外をスローします。
iterator = MyIterator([1, 2, 3, 4, 5])
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
## StopIteration
print(next(iterator))
出力:
1
2
3
4
## StopIteration
Python における反復子の一般的な使い方は以下の通りです。
ジェネレータは、関数を使って作成される特殊な種類の反復子です。関数を使って反復子を作成する簡単な方法です。
ジェネレータ関数は通常の関数と同じように定義されますが、値を返すために return
キーワードを使う代わりに、yield
キーワードを使います。ジェネレータ関数が呼び出されると、すぐに関数本体が実行されるわけではありません。代わりに、関数本体を必要に応じて実行するために使えるジェネレータオブジェクトを返します。
ジェネレータ関数は、その本体のどこにでも yield
文を持つことができます。ジェネレータ関数が呼び出されると、すぐに関数本体が実行されるわけではありません。代わりに、関数本体を必要に応じて実行するために使えるジェネレータオブジェクトを返します。
次は、数値のリストの二乗を生成するジェネレータ関数の例です。
def my_generator(data):
for x in data:
yield x**2
for x in my_generator([1, 2, 3, 4, 5]):
print(x)
出力:
1
4
9
16
25
ジェネレータは便利です。なぜなら、すべての要素を事前に生成するのではなく、必要に応じて要素を生成できるからです。これは、不要な要素を生成および格納することを回避できるため、より効率的なアプローチになる場合があります。
ジェネレータはまた、Python で遅延評価を実装するためにも使用されます。これは、ジェネレータの要素が必要になるまで生成されるだけで、事前にすべての要素を生成するわけではないことを意味します。これは、不要な要素を生成および格納することを回避できるため、より効率的なアプローチになる場合があります。
Python におけるジェネレータの一般的な使い方は以下の通りです。
反復子とジェネレータの主な違いは、それらが実装される方法です。
反復子は、2 つのメソッド __iter__
と __next__
を実装するオブジェクトです。__iter__
メソッドは反復子オブジェクト自体を返し、__next__
メソッドは反復子から次の値を返します。
ジェネレータは、値を返すために yield
キーワードを使う関数です。ジェネレータ関数が呼び出されると、すぐに関数本体が実行されるわけではありません。代わりに、関数本体を必要に応じて実行するために使えるジェネレータオブジェクトを返します。
以下は、反復子とジェネレータの主な違いのまとめです。
__iter__
と __next__
メソッドを実装するオブジェクトです。リスト、タプル、または文字列などの反復可能オブジェクトから作成されます。yield
キーワードを使う関数です。ジェネレータ関数を呼び出すことによって作成されます。全体的に、反復子とジェネレータの両方は、Python で要素のシーケンスを反復処理するための便利なツールです。これらは、すべての要素を事前に生成するよりも、1 回に 1 要素ずつシーケンスの要素にアクセスまたは生成することを可能にします。これは、より効率的である場合があります。
この例では、素数を生成するジェネレータを作成します。
まず、補助関数 _is_prime
を定義しましょう。この関数は、数が素数の場合は True
を返し、そうでない場合は False
を返します。
def _is_prime(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
次に、ジェネレータ関数 prime_numbers
を定義しましょう。
def prime_numbers(n):
for i in range(2, n+1):
if _is_prime(i):
yield i
ジェネレータをテストしてみましょう。
for prime in prime_numbers(20):
print(prime)
出力:
2
3
5
7
11
13
17
19
ジェネレータ式は、リスト内包表記に似ていますが、リストを作成する代わりにジェネレータオブジェクトを返します。
ジェネレータ式は丸括弧 ()
を使って定義され、1 つ以上の for
句を含むことができます。それは必要に応じて評価され、式の要素を必要に応じて生成するために使えるジェネレータオブジェクトを返します。
次は、数値のリストの二乗を生成するジェネレータ式の例です。
generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in generator:
print(x)
出力:
1
4
9
16
25
ジェネレータ式は便利です。なぜなら、すべての要素を事前に生成するのではなく、必要に応じて要素を生成できるからです。これは、不要な要素を生成および格納することを回避できるため、より効率的なアプローチになる場合があります。
ジェネレータ式はまた、Python で遅延評価を実装するためにも使用されます。これは、ジェネレータ式の要素が必要になるまで生成されるだけで、事前にすべての要素を生成するわけではないことを意味します。これは、不要な要素を生成および格納することを回避できるため、より効率的なアプローチになる場合があります。
Python におけるジェネレータ式の一般的な使い方は以下の通りです。
ジェネレータ式は Python における強力なツールであり、効率的でエレガントなコードを書くために使用できます。
次は、数値のリストの二乗を生成するリスト内包表記とジェネレータ式の例です。
## リスト内包表記
squares = [x**2 for x in [1, 2, 3, 4, 5]]
print(squares)
## ジェネレータ式
squares_generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in squares_generator:
print(x)
出力:
[1, 4, 9, 16, 25]
1
4
9
16
25
リスト内包表記とジェネレータ式にはいくつかの類似点と違いがあります。
for
句と要素を生成する式があります。全体的に、リスト内包表記とジェネレータ式の両方は、Python で要素のシーケンスを生成するための便利なツールです。リスト内包表記は一般的に高速で、より多くのメモリを使用します。一方、ジェネレータ式は一般的に低速で、より少ないメモリを使用します。どちらを使用するかは、アプリケーションの特定の要件に依存します。
この実験では、Python の組み込みの反復子、ジェネレータ、およびジェネレータ式について学びました。これらの構文がどのようにして Python で効率的でエレガントなコードを書くために使用できるかを見ました。また、ジェネレータを使って素数ジェネレータを実装する方法の例も見ました。