Python、はじめました

非IT系私文卒リーマン vs Python の全記録

Pythonの学習過程(その1)

『初めてのPython』でディクショナリについての章(記事は以下)を書いていたときの話。

python3.hatenadiary.jp

 

viewオブジェクトってなんだ?

例のごとく『初めてのPython(Python2.X準拠)』の.keys()メソッドについての記載が最新のPython3.xと異なるからネットで違いについて調べていた。

Python2.xでは.keys()メソッドはキーのlistオブジェクトを返す。

Python3.0以降では、.keys()メソッドはキーのdict_keyオブジェクトを返す。

この変更ってなんのためにあるんだ?ってかviewオブジェクトってなに?

それで気になって調べた結果、参考になったのがこのサイト。

www.atmarkit.co.jp

viewオブジェクトについて書いたこの部分が気になった。

Python 3で辞書オブジェクトのkeys()を呼び出すと、dict_keysという名前のオブジェクトが返ってきます。これがviewと呼ばれるタイプのオブジェクトです。イテレータと同じ働きをして、イテレーションのたびに次のオブジェクトを返します。自分自身では値を保持せず、呼び出し元の辞書オブジェクトにリファレンスを張るような形でキー一覧を返します。

 イテレータと同じ働き…ってかイテレータって何?

 

iteratorってなんだ?

イテレータについて気になったので、今度はイテレータについての記事を書くことに。それで書いたのが以下の記事。

python3.hatenadiary.jp

なるほど、iteratorとはつまり、iteration可能(iterable)なオブジェクトのなかでも、さらにnext関数によって値を吐き出すことができるオブジェクトのことなのか。実際動かしてみる。

L1 = [1,2,3,4,5]
L2 = [75,4,3,2,1]
D = dict(zip(L1,L2))
K = D.keys()
print(next(K))
>>>TypeError: 'dict_keys' object is not an iterator

は???dict_keysオブジェクトはiteratorじゃない??????!!

 

調べ方→5chプログラム板と知恵袋

いろいろ調べてみた結果、リンク先のPython3.0環境とぼくが使用しているPython3.6.5の環境におけるdict_keysオブジェクトの出力が違うことがヒントではないかと思った。

#リンク先Python3.0環境
K = D.keys()
print(K)
>>>dict_keys object at 0x33d9d0

#ぼくのPython3.6.5環境
K = D.keys()
print(K)
>>>dict_keys([1,2,3,4,5])

 ここで、何からそう思ったか今となってはわからないことだけど、以下のような推測を立てた。

出力するとdict_keys object at 0x33d9d0のような遅延評価(意味不明)の形で出るのがiteratorであり、Python3.0のころには間違えなくdict_keysオブジェクトはIteratorだった。最新版のPythonではそれがviewオブジェクトになってしまった。

聞ける人も周囲にいないので、5chのプログラム板に質問を書き込んだ。

【質問】

くだすれPython(超初心者用) その37

【回答】

くだすれPython(超初心者用) その37

 質問文が悪かったせいか、望むような回答は得られなかった。今度は質問文を詳しく書いて知恵袋に投稿する。

detail.chiebukuro.yahoo.co.jp

望む回答が得られた。以下の2点。

 

1.dict_keys object at 0x33d9d0という出力と遅延評価は全く関係ないということ。

Python3.0においてはdict_keysクラスは__reper__()の実装がない。(※オブジェクト指向そのため、print関数の引数にすると、オブジェクトの型の名前とアドレスが出力される。Python3.6.5については、help関数で調べてみると分かるように、dict_keysクラスには__repr__がreturn self の形で実装されている。

2. 組み込み関数 — Python 3.6.5 ドキュメント

 

2.dict_keysオブジェクトは未だかつてIteratorだったことはないこと。

.keys()メソッドで戻されるのは、Python3.0 より前はlistオブジェクトであり、Python3.0以降はviewオブジェクトに分類されるdict_keysオブジェクトである。これはIteratorではなく、リンク先のIteratorという説明は誤りである。詳しくはhelp(dict_keys)参照。

 

正しい調べ方→公式のリファレンスと歴代リファレンス

ちなみに、Python3.0の説明文にも、以下のような記載がある。

What’s New In Python 3.0 — Python v3.0 documentation

 Views And Iterators Instead Of  Lists

Some well-known APIs no longer return lists:

  • dict methods dict.keys()dict.items() and dict.values() return “views” instead of lists. For example, this no longer works: k = d.keys();k.sort(). Use k = sorted(d) instead (this works in Python 2.5 too and is just as efficient).
  • Also, the dict.iterkeys()dict.iteritems() and dict.itervalues() methods are no longer supported.
  • map() and filter() return iterators. If you really need a list, a quick fix is e.g. list(map(...)), but a better fix is often to use a list comprehension (especially when the original code uses lambda), or rewriting the code so it doesn’t need a list at all. Particularly tricky is map() invoked for the side effects of the function; the correct transformation is to use a regular for loop (since creating a list would just be wasteful).
  • range() now behaves like xrange() used to behave, except it works with values of arbitrary size. The latter no longer exists.
  • zip() now returns an iterator.

 

強調部からもわかるように、viewオブジェクトがIteratorの一種なら、map() and filter()のところと連続する形でviewオブジェクトの説明が入っているはずである。

 

結論

『初めてのPython(Python2.x準拠)』ではこう書かれているけど、Python3.xで動かしたら違う結果になった。どうしてだ?

と疑問に思うようなことがあったら、まず中途半端な知識でネットの海を漂うのではなく、まずは公式のリファレンスやその歴代の変遷を見たほうが良いと思った。

今回は、ディクショナリ→viewオブジェクト→Iterator→歴代リファレンス

の順で勉強になった。