3
0

More than 1 year has passed since last update.

Pythonでmapオブジェクトを複数回使おうとしたら怒られた

Last updated at Posted at 2022-02-04

該当のコード

data=[(155,48),(154,43),(157,46),(165,58),(161,49),(160,51),(158,44),(175,60),(163,47),(159,43),(159,44),(163,45),(155,44),(149,46),(160,48),(168,49),(163,55),(153,47),(162,51),(160,50),(159,47),(148,42),(165,56),(158,48),(170,54),(161,44)]
bmi = lambda x:round(x[1] / ((x[0]/100) ** 2))
map_obj=map(bmi,data)

print("最小BMI:",min(map_obj),
      "最大BMI:",max(map_obj))

これを実行するとエラーになります。

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    "最大BMI:",max(map_obj))
ValueError: max() arg is an empty sequence

なぜか

5行目のところですでにmapオブジェクト(イテレータ)が消費されているため、メモリからなくなっている。
なので6行目ではmax関数に渡せるものがなくなりエラーになる。

どうすべきか

listを使う

data=[(155,48),(154,43),(157,46),(165,58),(161,49),(160,51),(158,44),(175,60),(163,47),(159,43),(159,44),(163,45),(155,44),(149,46),(160,48),(168,49),(163,55),(153,47),(162,51),(160,50),(159,47),(148,42),(165,56),(158,48),(170,54),(161,44)]
bmi = lambda x:round(x[1] / ((x[0]/100) ** 2))
bmi_list=list(map(bmi,data))

print("最小BMI:",min(bmi_list),
      "最大BMI:",max(bmi_list))

Q:何でlist返さないの?

A:イテレータを使うほうがメモリに優しいから

listを返すとなると元のリストから加工したリストを全部メモリに保持しなくてはならない。
イテレータなら都度計算するのでメモリに優しい

比較してみる

for xxx in yyy のように普通のループとして使われるとメモリにどのような差があるのかを調べる。
999999回計算されるアイドルのBMI

リストを使う


from memory_profiler import profile
import time

data = [(155, 48)] * 999999


@profile
def main():
    start_time = time.perf_counter()

    def bmi(x): round(x[1] / ((x[0]/100) ** 2))
    list_obj = list(map(bmi, data))
    for result in list_obj:
        pass
    end_time = time.perf_counter()
    elapsed_time = end_time - start_time
    print("processed time:", elapsed_time)

main()

listを使わない


from memory_profiler import profile
import time

data = [(155, 48)] * 999999


@profile
def main():
    start_time = time.perf_counter()

    def bmi(x): round(x[1] / ((x[0]/100) ** 2))
    map_obj = map(bmi, data)
    for result in map_obj:
        pass
    end_time = time.perf_counter()
    elapsed_time = end_time - start_time
    print("processed time:", elapsed_time)

main()


結果

listなし

processed time: 239.2219612
Filename: test.py

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
     7     26.8 MiB     26.8 MiB           1   @profile
     8                                         def main():
     9     26.8 MiB      0.0 MiB           1       start_time = time.perf_counter()
    10
    11     26.8 MiB -75115.5 MiB     1999999       def bmi(x): round(x[1] / ((x[0]/100) ** 2))
    12     26.8 MiB      0.0 MiB           1       map_obj = map(bmi, data)
    13     26.8 MiB -37557.8 MiB     1000000       for result in map_obj:
    14     26.8 MiB -37557.8 MiB      999999           pass
    15     26.8 MiB     -0.1 MiB           1       end_time = time.perf_counter()
    16     26.8 MiB      0.0 MiB           1       elapsed_time = end_time - start_time
    17     26.8 MiB      0.0 MiB           1       print("processed time:", elapsed_time)

listあり

processed time: 238.8371256
Filename: test.py

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
     7     26.8 MiB     26.8 MiB           1   @profile
     8                                         def main():
     9     26.8 MiB      0.0 MiB           1       start_time = time.perf_counter()
    10
    11     35.2 MiB -92447.4 MiB     1999999       def bmi(x): round(x[1] / ((x[0]/100) ** 2))
    12     35.2 MiB      0.0 MiB           1       list_obj = list(map(bmi, data))
    13     35.2 MiB      0.0 MiB     1000000       for result in list_obj:
    14     35.2 MiB      0.0 MiB      999999           pass
    15     35.2 MiB      0.0 MiB           1       end_time = time.perf_counter()
    16     35.2 MiB      0.0 MiB           1       elapsed_time = end_time - start_time
    17     35.2 MiB      0.0 MiB           1       print("processed time:", elapsed_time)

感想

listを使った場合のほうがMem usageが増えています。
メモリをより動かすのはlistを使ったほうなのでしょうか。
ただ速度面で差はでていないのであまり考えるだけ無駄かも。

3
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0