質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

4回答

11898閲覧

enumerate object at 0x102376318と表示されることから、enumerate()のtupleがどういう形式で戻り値となっているかを解釈したが、正しいか判断してほしい。

gunmed

総合スコア55

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

1クリップ

投稿2019/02/09 06:54

pythonの入門書を一冊やった程度のプログラミング初学者です。
enumerate()関数を理解するためいろいろとコードを組んで実験していました。
enumerate()は引数にシーケンスを入れると、その順番に0から番号をつけ、tupleとしてかえすという解釈です。
そして以下のコードを書きました。

branches = [11, 'aa', 'fa'] print(list(enumerate(branches))) print(type(enumerate(branches))) print(enumerate(branches)) for a, b in enumerate(branches): print(a, b)
[(0, 11), (1, 'aa'), (2, 'fa')] <class 'enumerate'> <enumerate object at 0x102376318> 0 11 1 aa 2 fa

問題はprint(enumerate(branches))で自分の中ではtupleが返されると思っていたので、<enumerate object at 0x102376318>が理解できませんでした。
(0, 11), (1, 'aa'), (2, 'fa')
のようにenumerate()で作成されたtupleの要素ごとに返されると思っていました。
ここで、tupleとして返すの返すがどういった形で返しているのか疑問になってしまいました。

###<enumerate object at 0x102376318>となぜ表示されるのかを考察
そこで色々調べてみると結局enumerate()はイテレータで値を一つづつ取り出せるオブジェクトであることにたどり着きました。
listやrangeはイテレータが生成可能な関数らしいです。
<enumerate object at 0x102376318>の真相を知るべく、その3者を用いて以下のようなコードを作りました。

branches = [11, 'aa', 'fa'] # enumerateで実験 i = iter(enumerate(branches)) print(next(i)) o = enumerate(branches) print(next(o)) print(enumerate(branches)) # rangeで実験 i = iter(range(1,11)) print(next(i)) print(range(1,11)) print(i) # listで実験 s = iter(branches) print(next(s)) print(branches) print(s)
# enumerateの結果 (0, 11) (0, 11) <enumerate object at 0x106b19438> # rangeの結果 1 range(1, 11) <range_iterator object at 0x106afe9c0> # listの結果 11 [11, 'aa', 'fa'] <list_iterator object at 0x106ad52b0>

気づいたことはenumerate()はイテレータを生成するための関数iter()を使用しなくてもイテレータとして存在しているということです。なぜなら、o = enumerate(branches)でprint(next(o))を用いて値を取り出せているからです。

また、enumerate object at 0x106b19438のような出力結果はイテレータがprint()の引数であると起こるようです。なぜなら、iter()でlistとrangeを囲んでイテレータに変換しprintすると、print(enumerate(branches))と似たような形式で出力されています。

上記の結果からまとめると、最初のコードでprint(enumerate(branches))で自分が考えた結果(tupleが列挙されるという結果)にならなかったのは、enumerate()はtupleを返すという関数であるが、それはデータ型ではなく、すでにイテレータとして返しているので、<enumerate object at 0x102376318>という結果になった。結果として、print(enumerate(branches))はprint()の引数がイテレータとなってしまったので、、<enumerate object at 0x102376318>と表示された。
逆にlistはイテレータを生成することのできるデータ型なので、print(list)とすれば、データが出力される。

以上のような解釈なりましたが、どこかおかしい部分があれば指摘していただけるととても勉強になります。

最後に、print(range(1, 11))とすると、自分の考えでは、1から10の値が出力されると思ったのですが、ただ文字列かのように、range(1, 11)と出力されています。今回の質問の主題とはすこし離れてしまいますが、この点も疑問なので、ご教授いただけると幸いです。

mac 10.14.1

LouiS0616👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

<enumerate object at 0x102376318>となぜ表示されるのか

enumerate.__str__がそう返しているから。

listにしろrangeにしろ、文字列に変換されるとき(printの引数として渡したり、str関数(クラス)で明示的に文字列に変換したりといった場合)はまず__str__メソッドが呼ばれます。<enumerate object at 0x102376318>はその返り値です。

もう少し突っ込んだことを言えば、enumerate.__str__そのものは実は実装されておらず、すべての基底であるobjectクラスで実装されたobject.__str__が実行された結果、その表示になっています。

python

1>>> enumerate.__str__ 2<slot wrapper '__str__' of 'object' objects> 3>>> object.__str__ 4<slot wrapper '__str__' of 'object' objects> 5>>> enumerate.__str__ is object.__str__ 6True

この辺を読んでください。
4. 組み込み型 — Python 3.6.5 ドキュメント
2. 組み込み関数 — Python 3.6.5 ドキュメント

イテレータかどうか

nextすればすぐにわかる。というかnextできるものがイテレータです。

python

1>>> e = enumerate([0,1,2]) 2>>> next(e) # イテレータ 3(0, 0) 4>>> m = map(lambda x:x, [0,1,2]) 5>>> next(m) # イテレータ 60 7>>> r = range(3) 8>>> next(r) # じゃない 9Traceback (most recent call last): 10 File "<stdin>", line 1, in <module> 11TypeError: 'range' object is not an iterator 12>>> l = [0,1,2] 13>>> next(l) # じゃない 14Traceback (most recent call last): 15 File "<stdin>", line 1, in <module> 16TypeError: 'list' object is not an iterator

iterに渡せば返り値がイテレータになるものがイテラブルです。

どれくらい振る舞いが違うかというと、これくらい違いがあるのでけっこう注意が要ると思います。

python

1>>> e = enumerate([0,1,2]) 2>>> list(e) 3[(0, 0), (1, 1), (2, 2)] 4>>> list(e) # 2回目はイテレータを使い果たしているので空リストになる 5[] 6>>> r = range(3) 7>>> list(r) 8[0, 1, 2] 9>>> list(r) # あえて言えば内部的にlist(iter(r))のように取り扱われ、毎回イテラブル→イテレータ変換されるので空リストにならない 10[0, 1, 2]

投稿2019/02/09 07:49

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gunmed

2019/02/11 13:15

回答ありがとうございます。出先でパソコンがなかったので返信遅れてしまいました。 イテレータかどうかを調べる方法についても再認識できました。 また、イテラブルの振る舞いについても違いがあることが明確なりました。 とても勉強になりました。ありがとうございました。
guest

0

いくつか回答がついてますが納得いったでしょうか?

自分はhayataka2049さん回答が本質をついていると思いますが、初心者の方に回答内容がつかみづらいかもしれないとも感じました。そこでhaytaka2049さんの回答の中身が「なぜ」という点をコメントしてみようと思います。

print(x)はどういう機能か?

実はprint関数自身は大した機能を持ってません。文字列(str型のインスタンス)を標準出力へ印字することぐらいです。しかしxに文字列以外を渡しても印字できますよね?それはprint関数が引数xに対してstr(x)を用いて文字列へ強制的に変換してから印字するからです。さらに

print(enumerate([1]))

と書くとenumerate([1])を計算するのはprint関数の役目ではありません。Pythonのインタープリタは何かの関数を呼び出すとき、その引数をあらかじめ計算してから関数を呼び出します。

printはenumerateインスタンスが渡されたかlistインスタンスが渡されたかでなにか特別なことをやるわけではなく何が印字されるかはstr(printの引数)の結果によって決まります。

さらにstr(x)もxの型に応じてどんな文字列を結果とするか色々計算しているわけではありません。大雑把に言えば単にx.__str__()というメソッド呼び出しをしているだけです。

これがhayataka2049さん回答(enumerate.__str__がそう返しているから)の意味です。

なぜlistインスタンスをprintすると[1, 2]のように印字されるのか

[1, 2]つまりlistインスタンスの__str__メソッドの定義が次のようになっているからです。(本当の定義内容ではなく本件の説明のためのコードにしています)

python

1class list: 2 ... 3 def __str__(self): 4 s = "" 5 iterator = iter(self) 6 while True: 7 try: 8 s += ", " + repr(next(iterator)) 9 except: 10 break 11 return "[" + s[2:] + "]"

repr関数というのがでてきましたがこの関数は(今は)str関数と似たようなものだと思ってください。repr(x)str(x)と同様、x.__repr__()というメソッド呼び出しをした結果を返す関数です。

なぜlistインスタンスは要素をいちいち含めたような文字列を__str__で返しているかといえば「そういう結果とした方が、このlistインスタンスがどんな内容になっているかプログラマーが把握しやすいから」と考えてください。Pythonの設計者がそうしたければ__str__メソッドを"<list length=2>"のような文字列を返す仕様にもできたはずです。ただそうすると実際の内容を把握するのに一々for文などを使って調べなければならないので「全部要素を含めるような文字列を返すことにしよう」と決めただけなのです。

なぜenumerate(...)をprintすると[(0, ...), (1, ..)]にならないか

さてenumerate([1, 2])の結果はenumerate型のインスタンスになりますがenumerate型は質問者さんもおわかりのようにIteratorの一種になります。このenumerate型のインスタンスの__str__メソッドは「将来nextが呼び出されたときに結果として返される値の列を印字することはしない」ということになっていますがなぜそうなっているか考えてみましょう。以下にenumerateとほぼ同じことができる別の定義を示します(実はenumerateの定義はこのくらい簡単です)。__str__メソッドだけが本来のenumerateとは異なる定義になっている点にご注意を。

python

1class MyEnumerate: # 本来の定義を壊さないようにあえてクラス名(型名)を変えています。 2 def __init__(self, iterable): 3 self.iterator = iter(iterable) 4 self.ordinal = 0 5 6 def __next__(self): # next(このオブジェクト)とするとこのメソッドが呼び出される 7 value = (self.ordinal, next(self.iterator)) 8 self.ordinal += 1 9 return value 10 11 def __iter__(self): # iter(このオブジェクト)とやるとこのメソッドが呼び出される 12 return self 13 14 # ここより上はenumerateインスタンスがiteratorとして振舞えるようにするための定義 15 16 def __str__(self): # 前述のlist.__str__と同じ内容です 17 s = "" 18 iterator = iter(self) 19 while True: 20 try: 21 s += ", " + repr(next(iterator)) 22 except: 23 break 24 return "[" + s[2:] + "]" 25 26 27for i, e in MyEnumerate(['a', 'b']): 28 print('i={}, e={}'.format(i, e)) 29 30# => 31# i=0, e=a 32# i=1, e=b 33 34print(MyEnumerate(['a', 'b'])) # => [(0, 'a'), (1, 'b')]

上記のような定義だと質問者さんが一番最初に予想したとおりに印字されるます。しかし本物のenumerteはそうはしてません。nextを呼び出すと次々に異なる要素を返すことを思い出してください。つまり上記のような__str__メソッドの定義をしてしまうと「printしただけなのに、MyEnumerateインスタンスの状態がかわってしまう」のです。__str____repr__はprintで印字する際などに多用するメソッドですが例えばデバッグのために印字したいだけなのに、デバッグプリントの行を挿入するとそれ以降の計算結果が変わってしまうと困りますよね。そういう意味合いで自分自身の状態を変えずに済む範囲でインスタンスを表す情報を文字列として返すのが__str____repr__の暗黙的なルールになっていると思います。

enumeratorだけでなく一般のIteratorインスタンスも同様で、自分自身の要素を得るには自分自身の状態を変化させねばなりませんので__str____repr__で生成する文字列に「自分が何を列挙しようとしているか」を含くめることはなく、結果の文字列に含めれらる情報はほとんどありません。せいぜい自分自身の型が何かをプログラマーにわかるようにしておくのが関の山です。しかしもしそうならわざわざ__str__メソッドを定義する必要はなくなります。なぜなら「型名を結果とするような__str__メソッドは既にobject型で定義されているのでenumerate自身でわざわざ定義するまでもない」からです。object.__str__メソッドは
"<型名 object at 0xメモリーアドレス>"
というような文字列を返すように定義されてます。

enumerateは型ですが、Pythonの全ての型はobject型を継承してますのでobject型のインスタンスと同じ振る舞いでよいならメソッドを一々再定義しなくてよいのです。

上記がhayataka2049さんの回答にある
enumerate.__str__が定義されていない
・「どれくらい振る舞いが違うかというと、これくらい違いがあるのでけっこう注意が要る」
の背景です。

print(range(1, 11))はなぜ[1, 2, ..., 10]じゃないか

こちらもrangeインスタンスの__str__メソッドがそういう内容を結果にしているのが直接的な原因ですが、質問者さんが期待するとおり別に[1, 2, 3, ..., 10]と印字できないわけではありません。rangeインスタンスはIteratorではなくIterableでして前述のlist.__str__と同じ定義にしてやりさえすればそういう結果が得られます。なぜそうしないかといえば「一々要素を文字列に含めなくても、開始・終了・ステップの各値を印字するだけでこのインスタンスが何者かを表すには充分だ」と設計者が考えたからだと思います。range(1, 10)の結果は[1, 2, 3, ..., 10]のような各要素を全部保持したような(つまりlistのような)インスタンスにはなってません。開始値、終了値、ステップの値程度を記録したごく小さなインスタンスなのです。rangeの実装例を挙げてみます。若干ややこしいかもしれませんがよく見ると単純な実装であることがわかります。(なお本件の説明のため大幅に省略した内容ですし正確でない点があります。)

Python

1class MyRange: 2 def __init__(self, start, stop, step): 3 self.start, self.stop, self.step = start, stop, step 4 5 def __iter__(self): 6 return MyRangeIterator(self) 7 8 def __str__(self): 9 return "MyRange({}, {}, {})".format(self.start, self.stop, self.step) 10 11class MyRangeIterator: 12 def __init__(self, range): 13 self.range = range 14 self.value = range.start 15 16 def __next__(self): 17 if self.value < self.range.stop: 18 self.value += self.range.step 19 return self.value - self.range.step 20 else: 21 raise StopIteration() 22 23 def __iter__(self): 24 return self 25 26for i in MyRange(1, 6, 2): 27 print(i) 28# => 29# 1 30# 3 31# 5 32print(MyRange(1, 6, 2)) # => MyRange(1, 6, 2) 33print(iter(MyRange(1, 6, 2)) # => <__main__.MyRangeIterator object at 0x6fffabac0>

回答を書いてみて「どうも充分わかりやすく説明できてない」という感じは否めないです。Iterable/Iteratorの説明をするのにクラスや型の知識を前提とした説明になってしまっているからです。そのあたりは「雰囲気をなんとなくつかんでいただけたら」という思いで書いたのですが、かえってわかりづらかったらすみません。

なお上記に書いたことは既に質問者さんご自身の頭の中にほとんどあるような気もします・・・

投稿2019/02/11 11:40

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gunmed

2019/02/11 13:25

回答ありがとうございます。 出先でパソコンがなかったので返信遅れていました。 とてもボリューミーな回答ありがとうございます。 hayataka2049さんの回答はとても興味深かったですが、KSwordOfHasteさんの解説もとても勉強になりました。ただまだ理解が不十分なので、後日もう一度読んで理解を深めようと思います。その時に疑問点があれば質問いたしますのでよろしくお願いします。 取り急ぎ、感謝の意を表すために返信いたしました。ありがとうございました。
gunmed

2019/02/12 14:26

時間をかけて精読してみたのですが、自分の中の曖昧な部分が浮き彫りになり、とても勉強になりました。 かなり噛み砕くと、print()は__str__メソッドを呼び出しているだけで、enumerate()の__str__には自分が思っていたような定義がされていなかったからtupleの列挙は出力されなかったというですね。 本文を読んでみて、1点だけ正しく解釈しているか自信のない部分があります。 『printしただけなのに、MyEnumerateインスタンスの状態がかわってしまう』とおっしゃっていた部分が含まれる段落の内容についてです。"状態が変わる"というのが完全に掴めてないのですが、以下のように解釈しました。 __str__メソッドの内容に状態を変化するようなコードが書かれていた場合。 nextで異なる要素を返すという動作をしている状態にあったのに、途中でprint()を行い、__str__メソッドを呼び出すことで、インスタンスの状態が変化し、nextの動作に支障が生じる(例えば、今まで取り出した要素がリセットしてしまい、また要素を一から取り出してしまう)という問題が発生する。だから、__str__メソッドにはそういった自分自身の状態を変えるような定義をしないようにしている。 という解釈を自分の中でしてみたのですが、どうでしょうか?わかりづらい説明で申し訳ないのですが、回答よろしくお願いします。
KSwordOfHaste

2019/02/12 14:30

> 途中でprint()を行い、__str__メソッドを呼び出すことで、インスタンスの状態が変化し、nextの動作に支障が生じる はい、そのとおりです。なお > 例えば、今まで取り出した要素がリセットしてしまい、また要素を一から取り出してしまう こうなってくれていればまだ多少はいいのですが、実際の動作は「それ以上何も要素が取り出せなくなる」となります。
gunmed

2019/02/13 04:24

返信ありがとうございます。 実際の動作は取り出せなくなってしますのですね、勉強になります。 print()とはなんなのかというところから丁寧に説明していただきありがとうございました。 とても勉強になりました。
guest

0

ここに記載することは感覚的な解釈です。

enumerate(branches)は「中に何が入ってますか?」と問い合わせるまでenumerateオブジェクトなんです。print()type()は中身のことには関知しませんのでenumerateオブジェクト以外の何物にもなりません。一方でlist()やfor文、もしくはnext()みたいなものは、中に何が入ってますかと問い合わせるので、enumerateオブジェクトは一つずつタプルを返し、結果あなたが観測されているように振舞います。

投稿2019/02/09 15:12

YouheiSakurai

総合スコア6142

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gunmed

2019/02/11 13:17

回答ありがとうございます。出先でパソコンがなかったので返信遅れてしまいました。 とても内容を噛み砕いていただき、直感的な感覚で理解できました。 ありがとうございました。
guest

0

非常に深い考察だと思います。
間違っている点があるとすれば、次の一文でしょうか。

listやrangeはイテレータが生成可能な関数らしいです。

listはイテレータを返しません。
実際にリストオブジェクトをnext関数に放り込むと例外が発生することが確認できます。

Python

1>>> lst = [1, 2, 3] 2>>> next(lst) 3Traceback (most recent call last): 4 File "<stdin>", line 1, in <module> 5TypeError: 'list' object is not an iterator 6>>> 7>>> it = iter(lst) 8>>> next(it) 91 10>>> next(it) 112 12>>> next(it) 133 14>>> next(it) 15Traceback (most recent call last): 16 File "<stdin>", line 1, in <module> 17StopIteration

**追記: ** 改めて確かめると、rangeオブジェクトもイテレータじゃないですね。知らなかったです。


リストやタプルなど、反復可能な要素はイテラブルと呼ばれます。
イテラブルから実際に値を辿りたいときにイテレータが取得されます。

ややこしいのは、イテレータもイテラブルであることです。
任意のイテレータitは、iter(it)に対して自分自身を返します。(註)

註:
その気になればそのルールに反する独自のイテレータを作るのは可能です。
ただしやるべきでは無いです。

######iterator

...略...
イテレータは、そのイテレータオブジェクト自体を返す __iter__() メソッドを実装しなければならないので、イテレータは他の iterable を受理するほとんどの場所で利用できます。

引用元: Pythonドキュメント » 用語集

投稿2019/02/09 07:30

編集2019/02/09 07:58
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gunmed

2019/02/11 13:12

回答ありがとうございます。出先でパソコンがなかったので返信遅れてしまいました。 イテレータもイテラブルであるということでさらに理解が深まりました。 イテレータを返すか返さないかについてももう少し検討しようと思います。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問