プロが教える店舗&オフィスのセキュリティ対策術

Win10でpython3.10 勉強中。
map利用例でこんな現象あり、私だけ? それともこれが仕様?
#map(関数、リストやタプルなどのオブジェクト) 要素全てに同じ関数適用できる機能
今回の問題提起は、下記の現象です。
mapを使って、その結果の利用1回目OKなのに、2回目以降使えない。

a = [0,1,2,3]
def multi(x):
y = x*2
return y
aa = map(multi, a)
print(aa) #<map object at 0x000001F335A9A5F0>
print(list(aa)) #map結果利用1回目 [0, 2, 4, 6, 8]
print(list(aa)) #map結果利用2回目の結果は []
print(len(list(aa))) #0 なんと2回目以降にmap結果を使うことができない!!!

b = list(aa) #代入してみた
print(b) #[] 結果は同じ 2回目以降map結果使えない
print(list(aa)) #[] 結果は同じ 2回目以降map結果使えない
print(aa) #<map object at 0x000001F335A9A5F0>は変わらない

#リスト内包表記では?
print(a) # [0,1,2,3]
b = [i*2 for i in a]
print(b) # [0,2,4,6] リスト内包表記では、何度も使える

#再度map挑戦
aa = map(multi, a)
c = [i for i in list(aa)] #map結果の利用1回目
print(c) #[0,2,4,6] 目的通りの結果
d = [i for i in list(aa)] #map結果利用2回目 下記のように結果は[]
print(d) #[] 使えない

教えて!goo グレード

A 回答 (5件)

>next(aa) の意味がよくわかりません



list(aa)
すると、listはまず、結果オブジェクト[]を作り
next(aa)して0を得て、
結果オブジェクトに0をappendして[0]に更新し
rext(aa)して2を得て、
結果オブジェクトに2をappendして[0, 2]に更新し
next(aa)して4を得て、
結果オブジェクトに4をappendして[0, 2、4]に更新し
next(aa)して6を得て、
結果オブジェクトに6をappendして[0, 2、4、6]に更新し
next(aa)して例外があがるので
[0, 2, 4, 6]を返す
という動きになります。

もういちどlist(aa)すると
aaは既に処理の末尾に達しているため最初から例外を
あげるため、listは[]を返します。
    • good
    • 0
この回答へのお礼

ふーん、なんかfile.read()に似ていますね。
最後まで読み込むと、前には戻らない。
早速の解説、ありがとう!!!!

お礼日時:2022/06/18 10:35

No3です。


ちょっと言葉がおかしいので訂正。

iterable というのは、イテレータ、あるいはイテレータを取得できるオブジェクトの総称なので、リストもiterableです。

mapは処理結果では無く、処理を実行する為のイテレータを返す関数です。

aa=map(multi,a)
のaaはイテレータで
取得した直後に
next(aa)
とすると、mapはaの先頭の0を読んでmultiを呼び出し
0を返します。
もう一度
next(aa)
とすると、mapはaの2番目の1を読み、multiを呼び出し
2を返します。
nextを計5回呼ぶと例外が発生し
データの終了を知らせます。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
先の回答は理解できましたが、
今回のは私の理解能力を少し超えてるようです。
next(aa) の意味がよくわかりません。
下記の意味かなと? ちょっと違うような・・・
一回目の list(aa)は動作し、2倍のリストを返しましたが、
二回目の list(aa)は、 [] を返しました。

お礼日時:2022/06/18 05:11

aa=map(multi, a)


はiterableを返すから
aa=list(map(multi, a))
でリストの内包表現のようにリスト化できる。

逆に
b=(i*2 for i in a)
とかけば、内包表現でもiterable を返せます。

list化すると全ての結果がメモリに展開されるので
用途によっては効率が悪い。

iterableは値の列挙が一回しか出来ないが
map等のiterableを受け取ってiterableを返す
関数を複数繋げると、複数のデータ変換処理を
繋げてメモリを殆ど使わずに並列実行出来ます。
#mapはそういう処理のためにあります。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
報告、遅くなりました。申し訳ありません。
やってみたら、その通り。
map関数の成果をリストにしたものは何度でも使えるが、
map関数のオブジェクトは一度しか使えない、ことはわかりました。
どうしてそうなるのかは、私の理解能力を少し超えるようです。

ちなみに、下記になりました。
a= [1,2]
def multi(x):
....y = 2 * x
....return y
aa = map(multi, a)
print("aa = ", aa) ....#aa = <map object at 0x0000029211F4A5F0>
aa1 = list(aa)
print("aa1 = ", aa1) ....#aa1 = [2, 4] 結果のリスト
print("aa1 = ", aa1) ....#aa1 = [2, 4] 結果のリストは同じ
print("list(aa)2回目 = ", list(aa)) ....#= [] aa呼び出し2回目働かない

お礼日時:2022/06/18 04:57

> 知らないと、リスト内包表記で作ったのと同様に繰り返し使えるはずなのにと惑乱のドツボにはまるかも。



そうですね。その通りだと思います。
問題の本質は、破壊的変更を含む「副作用」がこっちの意図外の挙動をする場合がある、って事で、これも、仕様はさておき、そういう「ドツボにハマる可能性がある」一例だとは思いますね。

> 一回しか使えないというのは、file.read()と似ていますね。
read()も一度使うと、ファイルの最後にポインタが移って、それ以上は[]が返ってきます。初めてやったときは困ってしまいましたが、数字を入れるとそこまで読み込み、次にread()使うとその次から最後まで読み込むことで、ポインタが動くことに気が付きました。
> これもそんな感じなのでしょうか?

凄く大きく言うと、その通りです。ファイルの読み込みも「副作用」だし、入力のreadも「副作用」です。
それらも、Pythonみたいなラムダ式を持つプログラミング言語だと、thunkとして変数に束縛すれば、貴方が思ったような動作をしてくれるでしょう。
一回、実験してみる事をオススメします。

> なお、ラムダ式ということは、毎回リフレッシュして使うこととおなじ。
なので、下記のようなのを考えてみました。
> --直接都度利用--1行で書けるけど文字数多い----

この辺は原理的には全く仰ってる通りです。
ただ、この質問のポイントは恐らく「変数に束縛して使い回しが出来ないか」と言う辺りなんで、多分、無引数ラムダ式を用いたthunk利用の方がスマートじゃないか、とは思います。
じゃないと、貴方がおっしゃる通り、変数に束縛出来る、と言う旨味がなく、結果毎回ベタ書きしないとなりませんからね。

> --関数定義して都度利用--2行必要--

確かに、これが一番正攻法だとは思います。
    • good
    • 0
この回答へのお礼

早速、分かり易い補足の解説していただき、ありがとうございます。
mapの成果物が、2回目以降利用できない、というのが理解できなかったのが教えてに駆け込んだ理由でした。
mapに出会うまでは、関数などの結果は何度でも利用できていたため、混乱してしまいました。
pythonは、記事の切り抜きできると知り、Webスクレイピングしてみようと、軽い気持ちで始めたのですが、
わずか7行でネット新聞の見出しを掬い取れることや、
for in利用のリスト内包表記の簡潔さと働きに魅せられました。
ネットや本参考にやってますが、一歩進むと疑問噴出して二歩後退(笑)
えっちらおっちらやっています。
今回は、貴重なご指導、ありがとうございました。

お礼日時:2022/06/11 22:17

うん、面白いトコに気づきましたね。



原則、mapはイテラブルを返すイテレータなんですが、これは「中身が消費」されます(これがイテレータの仕様)。つまり、内情は「破壊的変更」が成されて、元には決して戻らない(だから、一部の人はこの機能を「遅延評価」と呼んでますが、厳密に言うと違います)。
これを避けるには、例えばmapの結果を無引数ラムダ式で包んでしまう、って手があります(こういう手法をthunkと呼んだりする)。

>>> def multi(x):
    return x * 2

>>> aa = lambda : map(multi, a)
>>> print(aa())
<map object at 0x7f2b36bc8c40>
>>> print(list(aa()))
[0, 2, 4, 6]
>>> print(list(aa()))
[0, 2, 4, 6]
>>> print(list(aa()))
[0, 2, 4, 6]
>>> print(list(aa()))
[0, 2, 4, 6]
>>> print(list(aa()))
[0, 2, 4, 6]
>>>

そうすれば、何度も再利用可能です。
    • good
    • 0
この回答へのお礼

アドバイス、ありがとう。
そんなことがあるのですか?!
知らないと、リスト内包表記で作ったのと同様に繰り返し使えるはずなのにと惑乱のドツボにはまるかも。
一回しか使えないというのは、file.read()と似ていますね。
read()も一度使うと、ファイルの最後にポインタが移って、それ以上は[]が返ってきます。初めてやったときは困ってしまいましたが、数字を入れるとそこまで読み込み、次にread()使うとその次から最後まで読み込むことで、ポインタが動くことに気が付きました。
これもそんな感じなのでしょうか?

なお、ラムダ式ということは、毎回リフレッシュして使うこととおなじ。
なので、下記のようなのを考えてみました。

a = [0,1,2,3]
print("---map関数を2回以上使えるようにする方法---")

def multi(x):
y = x*2
return y

print("--ラムダ式利用すると1行で書ける---")
aa = lambda : map(multi, a)
print(aa()) #<map object at 0x000002244221B460>
print(list(aa())) #[0, 2, 4, 6]
print(list(aa())) #[0, 2, 4, 6]
print(list(aa())) #[0, 2, 4, 6]

print("--直接都度利用--1行で書けるけど文字数多い----")
print(list(map(multi, a))) #[0, 2, 4, 6]
print(list(map(multi, a))) #[0, 2, 4, 6]

print("--関数定義して都度利用--2行必要--")
def bb():
return map(multi, a)

print(list(bb())) #[0, 2, 4, 6]
print(list(bb())) #[0, 2, 4, 6]
print(list(bb())) #[0, 2, 4, 6]

お礼日時:2022/06/11 14:42

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

教えて!goo グレード

このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング