Pythonに組み込みのmap関数を使って、反復可能オブジェクトの要素に対して、一括で何らかの処理を適用する方法を紹介する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
result = map(int, ['1', '2', '3']) # リスト(反復可能オブジェクト)の要素を整数化
# map関数の戻り値はイテレータ(mapオブジェクト)
print(result) # <map object at 0xXYZ...>
for item in result: # forループで処理する
print(item, type(item))
# 出力結果:
#1 <class 'int'>
#2 <class 'int'>
#3 <class 'int'>
# list関数でリストに変換する
result = map(int, ['1', '2', '3']) # list関数でイテレータをリストに変換
result = list(result)
print(result) # [1, 2, 3]
# 同じことをするリスト内包表記
result = [int(num) for num in ['1', '2', '3']]
# 第2引数には反復可能オブジェクトを指定する
result = map(int, '123') # 文字列は反復可能オブジェクト
# result = [int(num) for num in '123']
result = list(result) # イテレータをリストに変換
print(result) # [1, 2, 3]
# 辞書の要素
d = {'foo': '_foo_', 'bar': '_bar_', 'baz': '_baz_'}
result = map(str.upper, d) # キーを反復
# result = [item.upper() for item in d]
print(list(result)) # ['FOO', 'BAR', 'BAZ']
result = map(str.upper, d.values()) # 値を反復
# result = [item.upper() for item in d.values()]
print(list(result)) # ['_FOO_', '_BAR_', '_BAZ_']
result = map(lambda item: f'{item[0]} : {item[1]}', d.items()) # キー/値を反復
# result = [f'{item[0]} : {item[1]}' for item in d.items()]
print(list(result)) # ['foo : _foo_', 'bar : _bar_', 'baz : _baz_']
# 第1引数には呼び出し可能なオブジェクトを指定する
# ユーザー定義関数
def subtract_1(num):
return num - 1
result = map(subtract_1, [0, 1, 2])
# result = [subtract_1(item) for item in [0, 1, 2]]
print(list(result)) # [-1, 0, 1]
# ラムダ式
result = map(lambda x: x - 1, [0, 1, 2])
# result = [item - 1 for item in [0, 1, 2]]
print(list(result)) # [-1, 0, 1]
# 呼び出し可能なインスタンス
class Subtract:
def __init__(self, subtracter=1):
self.subtracter = subtracter
def __call__(self, num):
return num - self.subtracter
subtract_1_from = Subtract(1)
print(subtract_1_from(10)) # 9
result = map(subtract_1_from, [0, 1, 2])
# result = [subtract_1_from(item) for item in [0, 1, 2]]
print(list(result)) # [-1, 0, 1]
# 反復可能オブジェクトを複数指定
l1 = ['foo', 'bar', 'baz']
l2 = [0, 1, 2, 3]
result = map(lambda x, y: f'{x} and {y}', l1, l2)
# result = [f'{x} and {y}' for x, y in zip(l1, l2)]
print(list(result)) # ['foo and 0', 'bar and 1', 'baz and 2']
result = map(lambda x, y, z: f'{x} and {y} and {z}', l1, l2)
print(list(result)) # TypeError
Pythonのmap関数は、反復可能オブジェクト(iterable、イテラブル)に格納されている一連の要素に対して、何らかの処理を適用し、適用後の要素を反復するイテレータを返送する。
map(function, iterable, ……)
反復可能オブジェクトに適用する処理を第1引数に、処理を適用する反復可能オブジェクトを第2引数に指定する。第3引数以降にさらに反復可能オブジェクトを指定することも可能だ(後述)。
処理と反復可能オブジェクトを1つずつ指定するのが、map関数の基本的な使い方となる。以下に例を示す。
result = map(int, ['1', '2', '3']) # リストの要素を整数化
# map関数の戻り値はイテレータ(mapオブジェクト)
print(result) # <map object at 0xXYZ...>
ここでは反復可能オブジェクトとして数字を要素とするリストを、それに適用する処理としてint関数を指定している。これにより、整数化された要素を反復するイテレータが返される。そのため、戻り値をそのまま画面に出力しようとしても、上の最終行のように要素ではなく、戻り値であるイテレータ(mapオブジェクト)が出力されるだけである点には注意しよう。
イテレータの中身はforループで取り出したり、list関数でリストに変換したりできる。以下はforループでその要素と型を表示したところだ。int関数により、数字がint型のオブジェクトになったことが分かる。
for item in result: # forループで処理する
print(item, type(item))
# 出力結果:
#1 <class 'int'>
#2 <class 'int'>
#3 <class 'int'>
list関数でイテレータをリストに変換するには次のようにする(上のコードを実行した直後にlist(result)を実行しても、イテレータは既に要素を反復しつくしているので、生成されるのは空のリストとなる点に注意)。
result = map(int, ['1', '2', '3']) # list関数でイテレータをリストに変換
result = list(result)
print(result) # [1, 2, 3]
なお、Pythonでは一般にmap関数よりもリスト内包表記やジェネレーター式を使用することが推奨される。上と同じことをするリスト内包表記を以下に示す。
result = [int(num) for num in ['1', '2', '3']]
リスト内包表記はもちろんリストを生成し、map関数はイテレータを返す。大量のデータに一括で処理を適用し、その後、処理済みの要素を逐次的に処理したいといった場合にメモリ量が問題になるのであれば、map関数は選択肢として考えてもよいだろう。なお、以降ではリスト内包表記で同じことをするとどんなコードになるかをコメントアウトして示すことにする。
map関数の第2引数(以降)には、処理を適用する反復可能オブジェクト(イテラブル)を指定する。上で見たリストはその典型だが、Pythonでは多くのものが反復可能オブジェクトとして定義されている。
例えば、文字列は(変更不可能な)反復可能オブジェクトなので、以下のようにmap関数に渡すと、文字列を構成する個々の文字が反復され、第1引数に指定した関数に渡される。このとき、第1引数に指定する関数は、第2引数以降に指定する反復可能オブジェクトの数に等しい数の引数を持つ必要がある。例えば、第2引数に反復可能オブジェクトを指定している。つまり、ここではmap関数に渡している反復可能オブジェクトは1つだけなので、第1引数に指定する関数は引数1つを指定して呼び出せる形式でなければならない(int関数はこの条件を満たしている)。第2引数と第3引数に2つの反復可能オブジェクトを指定すれば、第1引数に指定する関数は2引数を指定して呼び出せる必要があるということだ。
result = map(int, '123') # 文字列は反復可能オブジェクト
# result = [int(num) for num in '123']
result = list(result) # イテレータをリストに変換
print(result) # [1, 2, 3]
辞書も反復可能オブジェクトだ。以下に辞書をmapに渡す例を示す。
d = {'foo': '_foo_', 'bar': '_bar_', 'baz': '_baz_'}
result = map(str.upper, d) # キーを反復
# result = [item.upper() for item in d]
print(list(result)) # ['FOO', 'BAR', 'BAZ']
辞書をそのまま渡した場合は、そのキーが反復される。よって、上の例では3つのキー('foo'、'bar'、'baz')がstr.upperメソッドに渡されて大文字化される('foo'.upper()、'bar'.upper()、'baz'.upper()が呼び出されるのに相当)。
辞書の値を反復するにはvaluesメソッドを呼び出して、辞書の値からなるビューオブジェクトを取得して、それをmap関数に渡す。以下に例を示す。
result = map(str.upper, d.values()) # 値を反復
# result = [item.upper() for item in d.values()]
print(list(result)) # ['_FOO_', '_BAR_', '_BAZ_']
キーと値の組を反復したいのであれば、itemsメソッドを呼び出して、キーと値で構成されるタプルを反復可能なビューオブジェクトを取得して、それをmap関数に渡す。以下に例を示す。
result = map(lambda item: f'{item[0]} : {item[1]}', d.items()) # キー/値を反復
# result = [f'{item[0]} : {item[1]}' for item in d.items()]
# result = [f'{k} : {v}' for k, v in d.items()]
print(list(result)) # ['foo : _foo_', 'bar : _bar_', 'baz : _baz_']
この例では第1引数にラムダ式を指定しているが、その引数が1つだけであることに注意しよう。itemsメソッドは2要素のタプルを反復する反復可能オブジェクトだが、map関数に渡している反復可能オブジェクトはあくまでも1つだけである。そのため、第1引数に指定する関数(ラムダ式)が受け取るのは1つの引数だけとなる。つまり、以下のようなコードは書けない。
result = map(lambda k, v: f'{k} : {v}', d.items())
result = list(result) # TypeError
このコードでは1行目を実行した時点では、ラムダ式の反復可能オブジェクトへの適用が遅延されているのでエラーとはならないが、2行目を実行した時点で「<lambda>() missing 1 required positional argument: 'v'」というメッセージと共にTypeError例外が発生する。これは「位置引数vに値が渡されていない」ということだ。そのため、最初のコードでは「lambda item: f'{item[0]} : {item[1]}'」のように、1つの引数にタプルを受け取り、「item[0]」「item[1]」のようにタプルの要素にアクセスしている。
今まで見てきたように、map関数の第1引数には組み込み関数やラムダ式など、呼び出し可能なものを指定できる。
以下はユーザー定義関数を渡す例だ。
def subtract_1(num):
return num - 1
result = map(subtract_1, [0, 1, 2])
# result = [subtract_1(item) for item in [0, 1, 2]]
print(list(result)) # [-1, 0, 1]
先ほども見たが、ラムダ式も渡せる。
result = map(lambda x: x - 1, [0, 1, 2])
# result = [item - 1 for item in [0, 1, 2]]
print(list(result)) # [-1, 0, 1]
__call__メソッドを実装しているクラスのインスタンスはかっこ「()」を付けて関数のように呼び出せるので、以下のクラスのインスタンスもmap関数に渡せる。
class Subtract:
def __init__(self, subtracter=1):
self.subtracter = subtracter
def __call__(self, num):
return num - self.subtracter
subtract_1_from = Subtract(1)
print(subtract_1_from(10)) # 9
result = map(subtract_1_from, [0, 1, 2])
# result = [subtract_1_from(item) for item in [0, 1, 2]]
print(list(result)) # [-1, 0, 1]
この例では、Subtractクラスのインスタンスを生成するときに、「subtract_1 = Subtract(1)」のように呼び出されたときに指定された値から減算する値を指定するクラスを定義している。そして、__call__メソッドは「subtract_1_from(10)」のようにかっこを付けて呼び出されたときに実行され、受け取った数値からインスタンス生成時に指定された値を減算した値を返す。
この呼び出し可能なオブジェクトも「result = map(subtract_1_from, [0, 1, 2])」のようにmap関数に渡せる。
最後にmap関数に反復可能オブジェクトを複数渡す例を示す。
l1 = ['foo', 'bar', 'baz']
l2 = [0, 1, 2, 3]
result = map(lambda x, y: f'{x} and {y}', l1, l2)
# result = [f'{x} and {y}' for x, y in zip(l1, l2)]
print(list(result)) # ['foo and 0', 'bar and 1', 'baz and 2']
既に述べているが、map関数に渡す反復可能オブジェクトの数と、map関数に渡す関数が持つ引数の数を一致させる必要がある。この場合は、2つの反復可能オブジェクト(l1とl2)をmap関数に渡しているので、それらに処理を適用する関数は引数を2つ持っている必要がある。そのため、上の例では2引数のラムダ式を指定している。
上と同様に反復可能オブジェクトは2つのまま、ラムダ式は3引数のものを指定すると例外が発生する。
result = map(lambda x, y, z: f'{x} and {y} and {z}', l1, l2)
print(list(result)) # TypeError
Copyright© Digital Advantage Corp. All Rights Reserved.