Fluent Python 読書メモ

はじめに

Fluent Pythonを読むにあたって、「本を読む本」のなかで問われている問いに答える形で、読書メモを作っていこうと思う。

  1. 「初級読書」 — これは、「その文は何を述べているか」を確認するのであるが、誤読していることもあるので侮れない。まま、このレベルに戻ることもあろう。
    1. 「読み方準備期」
    2. ごく簡単な読み方を覚える
    3. 文脈をたどって知らない単語の意味をつかむ。
    4. 1つの作品から得た概念を消化して次の書籍を読む。1つの主題について幾人かの著者が述べることを比較することができるようになる。(ここに至ることができないこともある)
  2. 「点検読書」 — 時間に重点を置いて、「系統だって拾い読み」をする技術を指す。今回は、緩急を付けて読んでいるので、これに該当するとおもう。
    • 「この本は何について書いたものであるか」
    • 「この本はどのように構成されているか」
    • 「どのような部分に分けられているか」
    • 「この本は、どういう種類の本か」
  3. 分析読書
  4. シントピカル読書」(他の本と比較しつつ読む)

と、「批判的読書のコツ 20のポイント」も押さえたい。あと、分析読書をいうか、内容を噛み砕く時の手がかり、足がかりのメモも残したい。

対象としている読者

page xiiにある。

python以外から、pythonを書くようになったプログラマーが、これまで培ったプログラミングのノウハウを越えて、「pythonらしい」 プログラムを書きたい場合に参照する本であり、python3を使いこなしたい人向けであるとのこと。

一定以上pythonを理解している必要があるので、読者はオススメしてあるドキュメントやチュートリアルをやっておく必要があるかもしれない。

また、python初心者がこの本を読むと、なんでもこの本に書いてあることを実践したくなるので、止めた方がいいともある。知っていて使わないという選択ができないと有害でもある。


yabuki (注)

読み進めるにつれて、対象の読者は、pythonの一通りについて分かっていて、用語もばっちりokという感じでないと、ふんふんと読み進めることは厳しいかも。こうやって読書メモを作る習慣がないと読みきるのも辛いかも。一人じゃなくて仲間で分担したほうがいいね。

それか、分からないことを調べるのをグッと我慢して、ある程度読み進めてから目星う付けて調べるでもいいか。うまく緩急を付けるのがコツか。

知っていた方がいいこと

  • REPL — 対話型コンソールについては、ちょっとしたプログラムを動かすのに便利だ。
  • doctest

あと、紙の本に落とす時にリンクは別にしてあるという方針なので、こうやって自分なりにそのlink集を再現した方が理解がしやすくなると思う。オンライン・ドキュメントを読んで理解が深まるなら作業は増えても、うんうん唸る時間は減る。

用語集

本文を読む前にざっとでいいから目を通しておくと、どの言葉が用語集にあるのか分かってよい。紙の文書だから、使っている用語をそのままハイパーリンクにできないのでこうしているのだと思うけど。

用語集が p711 からある。私のように用語の定義というか何を指しているのかを知りたがる人は先に目を通しておこう。 2020-07-12 の 06:10 時点で4章以前の疑問に思ったことはだいたい書いてある。最初から気がついていたら時間の節約もそうだし、悩む必要がなかった。

あと、本家の用語集(の日本語版)も目を通しておけとのこと。

点検読書I

「この本は何について書いたものであるか」

(最後まで読み切れていないが、暫定版として)この本は、pythonの持っている力を引き出すメンタルモデルと、プログラミング方法について書いた本である。

「この本はどのように構成されているか」

まえがきのページ xii からの書いてある。拾い読みをするときに、いいガイドになるかも知れません。ただ、理解するには前に遡って読む必要があるかもしれません。

I部

II部

III部

IV部

V部

VI部

「どのような部分に分けられているか」

「この本は、どういう種類の本か」

pythonの能力を引き出す方法、pythonic? な方法について書いた本

1章 Pythonのデータモデル

pythonのデータモデルについて説明なしに知っている前提で書いています。ひとまずいろいろ棚上げして、辛抱強く最後まで読むとなんとなく分かります。一緒に、3.データモデルとか眺めておくといいかもしれません。

データモデルとは、何ぞやというのは、別の本でちゃんと定義されているのかもしれません。この章を読むと書いてある内容からなんとなく、「データモデル」とよんでいる物が、Pythonの根幹を為す、ダンダー1についての話なんだなって。

Soapbox の、「データモデル?それともオブジェクトモデル?」で、もう少しヒントがでてくる。

Soapboxでは、他にjsとjavaの話がでてる。p16からp17にかけて。

lenがメソッドでないのは、そうとう議論になったから何度も出てくるんだろうね。やはり。

1.1 Pythonicなトランプ

  • collections — コンテナデータ型 — Python 3.7.8 ドキュメント

  • 2〜10,J,Q,K,Aの配列

  • namedtupleの利点は、固有のメソッドを持たないオブジェクトをのクラスを作れるようになった。以下は矢吹の推測だが、なんでもオブジェクトだとやりすぎで、シンプルなデータ構造を作ってメモリの有効活用をしたかったのではないか。

シントピカル読書
Effective Pythonのp59で、namedtupleの限界というコラムがある。有用な場合と害の場合がある。とのこと。

疑問点

  • Pythonのデータモデルとは、どのように説明したらいいのか。

読書会で、話題になったこと。

chatに貼り付けてもらったとか、話題にでたのを主に書き残しておく。

2章 シーケンスの配列

pythonに関係深い、ABCという言語の話が導入となる。

シーケンスって、なんだったかな。と、Python ボケットリファレンスの113pを参照しておく。クラス図からの説明になる。きっちりと言葉で説明するのは難しいが、こうやってクラス図で表されると何となくわかる。でも他人に説明できるかと言われると多分できない。

データの入った物が一列に並んでいるイメージを想定してください。って感じか。コンテナが並んでいる感じ。

2.1 組み込み型シーケンスの概要

で、シーケンスとはなにか?については既知として、分類から概要が始まる。

  • コンテナ・シーケンス
  • フラット・シーケンス

p22を参照。参照が入るか、実体のデータたちがはいるか。それぞれに、メリットとデメリットがある。

別の分け方として、

  • mutable sequence
  • immutable sequence

可変なシーケンス、不変なシーケンスという日本語がわりあたっているが、昨今ではカタカナの方が通りがよかったりする?そして書き換えができるか、できないか。の違いなので、自分のイメージにひきつけて理解することにした。

そして、図2-1 この図に見慣れないと、後から混乱しそう。向かって右が、子で、向かって左が、親の関係か。ここでは子から親に矢印が向かっている。と理解した。p23

2.2 リスト内包表記とジェネレータ式

シーケンスを作る手段として、リスト内包表記と、ジェネレータ式に言及じている。「読みやすくて速い」という著者の主張を読み進めることになる。

下記は省略語として、

  • listcomp : リスト内包表記
  • genexp : ジェネレータ式

という表現を知る。

p24の構文のヒントは役立った。ここで改行していいってのは、コードを見てなんとなくは分かっていたが文章で書いてもらえるのはありがたい。

p25のコラムは、python3の世界になったので、今となっては、python2のコードを書く必要がないかぎり忘れて良いのではないか。

2.2.2 リスト内包表記とmap/filterの違い

pythonのlambdaか、機能的に足りないとは? 別の場所で言及はあるか。あと、何と比較して足りる足りないを著者は語っているのか。

例2-3のコードが、リスト内包表記とmap/filterで書いたコードを比較している。若干慣れの気がするかもしれないが、実行速度を比較するコード listcomp_speed.py があるので、差が分かるぐらいに違うんだろう。

2.2.3 デカルト積

デカルト積という名前に、不安を抱く必要は無い。数学の順列・組み合わせを思い出せば良いみたい。または行列の積

2.2.4 ジェネレータ式

全部を一度に作らなくても、実行するたびに値を返せば、使用メモリーを減らすことができるのは道理だ。

あと、ポケットリファレンスでは、yield文を使う例しか出てこなかったが、ここでは違う書き方があるで勉強になる。

2.3.1 レコードとしてのタプル

RDBをよく使っていると、レコードというと、RDBで処理したらいいじゃないか。と、してしまいそうになるが、ここで書いてあるテクニックは、pythonらしい書き方というか、データ操作方法になると思う。ある程度データが大きくなるまではRDBへの依存がないプログラムの方が扱いが楽だし。依存を増やすのは後からでもできる。

2.3.2 タプルのアンパック

ここでの、アスタリスクの使い方は、おまじないだとして引数などに付けていた、アスタリスクが、vividに意味を持ち始めた。あとタプルの値のスワップに、一時変数を使わなくても良いってのは、C言語からすると楽ではあるが、ちょっと落ち着かない。(内部ではちゃんとしているだろうから便利に使わせてもらうのてはある)

_ は、文法でなく慣習であるというのは、前に調べていたのでよかった。また i18n への言及があるのは良い。ただ、これだけではない。p32

2.3.3 ネストしたタプルのアンパック

ちょっとしたRDBの問い合わせみたいなことをここでは、タプルとアンパックを使って実現してる。p33

2.3.4 名前付きタプル

僅かなメモリの増加を受け入れると、名前付きのタプルでこんな便利なことができるよ。と教えてくれる。プログラムをを書くにあたって確かに明確に意志を伝えるプログラムを書くことができるので使いたくなる。

本の範囲は外れるが、 python3.7からは、 dataclasses — データクラス — Python 3.7.8 ドキュメント とかもあるのでデータを扱う引き出しが増えるので、ここにメモっておく。

2.3.5 不変リストとしてのタプル

不変リスト = immutable

listとtupleの似ている点について、どこまで一緒なのかを確認すると違う部分に集中できるので良さそう。p35からp36

2.4 スライス

ここで扱うのは、python本体と一緒に配布されているライブラリの使い方であり、自分で同じような動作をするクラスを作るのは、第10章で扱うとのこと。

ここでは、あんまりできると思っていなかった使い方を知る。ごちゃごちゃ書かずにスパッとデータを書き換える方法がある。p39

NumPyと、SciPyについては、例外として言及している。p38

2.5.1 リストのリストの生成

ここは、実体を回数分生成しているのか、参照を回数分生成しているのか。を、必要な時には思い出したい。p41

2.6.1 +=による代入の謎

へー。としか。確かにコーナーケースである。

2.7 list.sortと組み込み関数 sorted

Noneが返ると、chain methodにならない。逆説的に、chain methodを作る方法とするのがいいか。p45

2.8 bisectを使った順序付きシーケンスの処理

大量のデータから、できるだけ速く該当のデータを探し出すために、あるキー順にソートしてある前提でデータ操作をすることがある。

身近な例でいうと、git bisect がキーワードとして似ているし、二分木探索の話はどのアルゴリズムの本なら触れているだろう。これまで読んだ本でおもしろかったのは、debugに、2分木法を使うという本を読んだときであった。

2.9 listを使わない方がよいケース

こいつ使って、いつ使わないかも知りたかった、

2.9.1 配列

ここも読む価値がある。

2.9.2 メモリービュー

Cのstructとunionの合わせ技みたいなことができるんや。

例 2-21に対するメモ

echo "obase=16;ibase=10;1024"|bc
400

2.9.3 NumPyとSciPy

この本では、基本的にはPythonに付属のライブラリの話をしているが、ここではタイトルの用に、NumPyとSciPyを扱う。

私は、Debian GNU/Linuxを使っており、お手軽に導入するため、apt install python3-numpy python3-scipyのコマンドで導入した。

2.9.4 デックやその他のキュー

appendやpopメソッドを使えば、スタックやキューとして使えるし、inque,dequeについても書いてある。スレッドセーフなのが嬉しい。

ここも、スレッドセーフ、プロセス間通信、asyncioに対応した。queue関係の情報かある。そしてheapqがあるので一から作らなくても、シーケンスをヒープキューや優先順序付きのキューを提供してくれるとのことで、自分で書いてデバッグする時間を減らすことができそう。

2.10 本章のまとめ

一度読んでおけば、ここから情報を取るだけで思い出せることが増えて良さそう。p61-p62

2.11 参考文献

ここは、また戻ってきた時に参照する。一回目は軽くなぞるだけにする。

Soapboxの「すばらしいkey」については、心惹かれた。

3章 ディクショナリとセット

この章は、最初に概要を書いてくれている。良い。

pythonを使っていて、辞書(ディクショナリ/dict)型を使わないことはない。と言いきってもいいぐらい。ユーザーが作っているプログラムで多用されているだけでなく、

  • モジュールの名前空間
  • クラスとインスタンスの属性
  • 関数のキーワード引数

などのpythonを支えている機能は、dict型で実装されている。これらはハッシュテーブル で作られていることが明かされる。

この章では、ハッシュテーブルの仕組みを解説することで、pythonのdictとsetを活用する方法を学ぶことができるとある。

3.1 一般的なマップ型

map型とは一体 :thinking face: なりながら、読み進める。

p70の図3-1

Mappingと、MutableMappingの継承について書いてあるから、これがmap型の説明なんだと思う。言葉でなく、UML図で示す感じ。どういうメンバーというかメソッドがいるのか、ってのを理解するのはこっちの方がまどろっこしくないからいいのか。

特殊用途のマップの実装について、ここに記述がある。「抽象基底クラス」ではなく、dictまたは、collections.UserDictを拡張することについて書いてある。

dict型かどうかを検査するより、isinstanceを用いよ。という話

「ハッシュ可能とは」というコラムはp70からp71 オブジェクト比較の話だが、比較できない場合について述べてあるので、いつ使えて、いつ使えないのかを確認するために参照することになるかも。内部状態という概念は得た。

  • 組み込み型 — Python 3.7.8 ドキュメント
    • 文書の中で言及しているは、この部分である。
    • ここの文書も一階層あがって読みきったから、この本書いているんだろうな。
    • 日本語版をpointしているので、4. Built-in Typesかどうかは見てない。例が同じそうだ。というのは軽く確認した。

3.2 ディクショナリ内包表記

リスト内包表記があるなら、ディクショナリも同じような書き方ができると類推できるよね。ってことか。p72

一般的なマップメソッドの概要

ここでも出てくるAPIという言葉。メソッドと何が違うのか、最初で定義しているのに読み飛ばしたかなと思えてきた。

ここの表も必要があれば参照したくなるだろう。dict,defaultdict,OrderedDictの差がわかる。

3.3.1 存在しないキーをsetdefaultで処理

ここで、「フェールファースト」の理念が出てくる。私はあんまり聞いたことがなかったので、監訳者の注で意味が補われていて予想した意味と異なっておらず助かった。ダックタイピングはけっこう聞くのでそこは気にならず。

この、setdefaultに関しては、持っているPythonポケットリファレンスの、P154 「9-3-8 デフォルト値付きの抽出」で、大まかな内容を知っているので、細かく挙動を書いてあるぽいことだけ確認した。

p75-o75 まずは、setdefaultを使わない方法を示して、p76で、setdefaultを使って解きなおししている。

3.4 柔軟なキー検索を使ったマップ

サーチしたキーが、存在しなかった時に、どういう扱いがいいかについて、2つの方法を提示している。

  1. dictの代わりに、defautldictを使う
  2. サブクラスを作って、__missing__ メソッドを追加する。

3.4.1 defaultdict — 存在しないキーの扱い方 その1

ここでは、サーチして最初に見つかったときに、エントリーを生成するプログラムを例として説明している。ようするに、KeyErrorを発生させない。

p78 のいつ呼び出されて、どういうときに呼び出されないか。は押さえておきたい。

3.4.2 __missing__ メソッド — 存在しないキーの扱い方 その2

__missing__ の定義の方法と、どのタイミングで、呼び出されるのかについて記述してある。これ、pythonの基礎的なメカニズムなんで、後から広く応用できるんではないだろうか。rubyのmissing_methodのように。読み進めると分かるか。

これについても、何時使えて、いつ使えないのか書いてあるので、ここは後からも参照しそうな感じ。p78

__missing__ の実装で、isinstance のチェックが必要な理由についても読んでおきたい。

3.5 dictのバリエーション

  • collections.OrderedDictの説明 p81
    • 挿入された順、デフォルトはqueueのような動作だが、stackのような動作をさせることもできる。
  • collections.ChainMap の説明 p81
    • 複数のマップを一括でサーチできる。サクッと作って後からチューニングするのに便利そう。どう使うかの例は書いてある。
  • collections.Counter の説明 p82
    • 最頻をカウントするのに便利そう。いくつか便利そうなメソッドも紹介している。
  • collections.UserDict
    • 次の 3.6 で説明とのこと。

3.6 UserDictのサブクラス化

こっちを使う方が便利なんじゃないかなっていう指針を教えてくれる。UserDictを使う前には、読んでおくとハマらずに短い時間でプログラムを作れそう。

3.7 不変マップ

immutable map 何時使うのか。についてはアイディアや必要性に迫られた時でいいか。その時に細かく読めば良いか。

3.8 セット

ポケットリファレンスのP155からp156にざっくりと書いてあるのを復習してからこの 3.8 を読む。

setについて。記述が古いのかあんまり使われていないとあるが、ハマると便利なんで使いこなしたい。classとしての、setとfrozenset

  • 重複した要素を許さない。
    • これは、RDBの一意制約にも似て、使いでがある制約に感じる。
    • 本文中の「重複する要素を削除することが」2ってのは、結果としてそうなる。という意味に取らないと削除する動作を何時するんだってなりそう。
  • Setの要素は、ハッシュ可能であること。Set自体はハッシュ不可能、Frozensetはハッシュ可能なので、Setの要素に、Set自身は使えないが、Frozensetは使える。
  • このへんになってくると、数学の集合論を利用したくなるね。集合論的な操作できるし。ループや条件分岐を減らせるのは、コードが短くなって間違いの余地が減りそう。

3.8.1 リテラル

Setの構文で、だいたい数学と一緒なんだけど、空集合だけは別の書き方をするぜ。{}とかくと、意図に反して空のdictができてしまう。これって、間違えないようにする工夫はあるだろうか。型?

リテラルなset構文は、慣れれば意図は明解なので良いという主張。コンストラクターを使う呼び出し方法と、リテラルの処理で、BUILD_SETバイトコードの実行の話になる。p87

バイトコードのdisアセンブラを見せながらの解説をしてくれる。

Frozensetには、リテラル表記はない。のでコンストラクタを呼び出すしかないとのこと。

3.8.2 セット内包表記

をを、確かにセットも内包表記があってしかるべきだよな。p88

3.8,3 セットの演算

ここで出てくる、「インプレイスで変更/更新」ってあんまり聞かない表現なんだけど。p88

ここで出てくるsetの操作をうまく使うとコードを短くできそう。

3.9 dictとsetの内部構造

内部構造の話だから、読み飛ばそうかなって思ったんだけど、性能やトレードオフの問題が書いてあるので、読み飛ばせなかった。ある程度経験を積んだプログラマなら気になることが書いてある。

この節では、

  • Pythonのdictとsetはどれだけ効率的なのか。
  • なぜ順序付きではないのか。
  • 任意のPythonオブジェクトをdictのキーまたは、setの要素に使えないのはなぜなのか。
  • dictのキーやsetの要素の順序が、挿入順に依存し、その構造が利用している間に変化することもあるのはどうしてなのか
  • dictやsetに対してイテレーションを行っているとき、それらに要素を追加してはいけないのはなぜなのか

を、説明してくれる。とのこと。内部構造を知ることで、その得失点と(pythonの)低レイヤを意識することで、性能と仕様を満たすバランスを考えることになるんだろうな。という感じか。メモを書きながら読んでいるので、後で訂正するかも。

3.9.1 性能評価実験

じぶんのpython3の環境で、追試してみるのも良いが、いまではない。

3.9.2 ディクショナリのハッシュテーブル

3.9.3 dictの構造に起因する実用上の影響

ここは一読しただけでは、よく分からない。将来のためにメモだけ残しておく。

  • p97 「ユーザ定義型のハッシュ値は、id()であり、それらを比較した結果はいつも等しくないため、元からハッシュ可能です。」って、「キーはハッシュ可能なオブジェクトでならなくてはいけない」とぱっと見に反しているので、補足が欲しい気がする。
    • 等しいということは、「ハッシュが同じ」である。__eq__() を実装する時に注意が必要っぽい。
  • dictには顕著なメモリーオーバーヘッドがある。
    • ここは重要で、どうしたら良いのかの提案もある。ただし書いてあるように、早すぎた最適化は保守性を下げるので、手早く富豪的アプローチで作ってから最適化を考えるというのも動かさないと評価できないので、一つの知見でもあろう。
  • キーサーチは非常に高速ってのは、スペースと時間がトレードオフになっている。アクセス時間はサイズに関係ない。サイズを指数的に大きくしても、影響はすくない。
    • ちゅうことは、この辺が実用的なpythonのプログラムを作るに当たっての勘どころなのか?
  • dictに要素を追加するとキーの既存の順序が変更される可能性がある
    • これ知っておかないとハマるだろう。既存と更新用に分けたら良いだけ。富豪的アプローチだけど副作用を考えたらね。こんな感じで困るぐらいカツカツの環境なら、言語とか仕様とか考え直して実装したほうがいいんじゃないだろうか。まずはpythonでつくって仮説を検証するでかなりの所までいけるでしょう。

3.9.4 セットの挙動

これまで記述してある内容の重複をさけるためにあっさりとしているが、これもsetを使う上では勘案することである。p99-p100

3.10 本章のまとめ

ここも、一度通読してから、読み直して頭に定着させるためにいい感じ。

3.11 参考文献

あとでリンク貼る。

ここのSoapbox p101-p102 は、いまいち理解できず。ハッシュの機構がシンプルだというのは、同意できるが、正確だというのは、何に対してどう正確なのか、理解できてない。他の言語の記述方法に似ていることと正確さに何の関連があるのか。正確というのは何かの基準があってそこへの差分が一定範囲内だという理解なので、いまいちピンときていないのだと思う。

4章 テキストとバイト

ここは、どのぐらいの事前知識を必要とするのだろうか。Unicodeのの時代だし、CJKに深く突っ込む所でもないから、文字コード本みたいな副読本はいらないかもしれない。とか思ったが、そんなことなさそう。Unidodeのバージョンの話がでてくるから、制定の話と改訂の話ぐらいは知っておくのは、この本を読む前に知っとけということ。

文字とバイトを区別するという話なので、まずは読み進めてみる。下記のトピックについて解説するとある。

  • 文字、コードポイント、およびバイト表現
  • bytes, bytearrary,memoryviewといったバイナリーシーケンス固有の機能
  • Unicodeおよび従来型の文字セットの完全なコーデック
  • エンコーディングエラーの回避と処置
  • テキストファイル処理におけるベストプラクティス
  • デフォルトエンコーディングと標準入出力の問題
  • 正規化による安全なUnicodeテキストの比較
  • 正規化、ケースフォールディング、付加記号を強引に除去するユーティリティ関数
  • localeとPyUCAライブラリを用いたUnicodeテキストの的確なソート
  • 文字とメタデータを収録したUnicodeデータベース
  • strとbytesを処理するデュアルモードAPI

はい。もうこの段階で、Codecが具体的に何を指しているのか、とか、Unicodeの正規化について具体的なイメージが沸かないので中身を読み進めたくなってきました。 ケースフォールディングに関しては、監訳者の注があるので意味が分かりました。MySQLとかでも問題になっていた件をpythonでどう解決するではないかと思いましたが、当たっているかどうかは読み進めてみないと分かりません。最初印象ってか予期しているバイアスを書いておいて後から自分で修正するスタイルで。

4.1 文字の問題

Unicodeをpythonで扱ってみて、皆さん慣れてくださいね。のパートです。

もうpython3に移行していると思うので、python2の記述は過去の資産をメンテする必要がなければ、読み飛ばしていきます。

4.2 バイトについて

C言語をやったことがあれば、unsigned charとか、unsigned char * で操作してる感じよね。って感じの話っぽい。低レベルな技術者3なら、するすると読み下せそう。

ここで出てくる、encodeとdecodeの例えは、図があったほうがいいな。本に落書きするか。

4.2.1 structとメモリビュー

これ、まんまC言語のstructで型にハメるやりかたや。memoryviewでunionってか参照させる感じなんや。こんな低レイヤなことできるんだな。

4.3 基本的なエンコーダとデコーダ

エンコーダとデコーダの両方を指す言葉として、コーデックと呼んでいるのがここに来てわかった。

参考になるが、CJKの本ではないので、日本語を扱うのであれば、Linux上でつかっているし、utf-8一択だな。発展的に調べるとCP932とかも多分あるんだろうけど。

サロゲートペアの説明をp111でしているのは良い。

4.4 エンコードとデコードの問題点

以下で個別に、エラーへの対処策が書いてあるので、困ったときにはそこを読めば良いってのは、困っている人向けに書いているからだろうな。

4.4.1 UnicodeEncodeErrorへの対処策

P112-p112

4.4.2 UnicodeDecodeerrorへの対処策

p113-p114

4.4.3 予期しないエンコーディングでモジュールをロードしたときのSyntaxError

p114-p115

コラムの「ソースコードでASCII文字でない名前を使ってよいものでしょうか」はおもしろい が使っていいときと使ってよくない時があるし、思っている以上に使われる可能性があるなら やめとけなんだろうね。

4.4.4 バイトシーケンスのエンコーディングを知る方法

そりゃそうだろうな。知っている所から(生成側)から教えないと正確な所は分かるまい。できても推測までで、身近な例だとブラウザがやっている。長い文字列なら判別できるが、短いと文字化けした経験はあるだろう。

4.4.5 便利だけど厄介なBOM

UTF16エンコードのBOMの話です。GNU/LinuxならUTF8で幸せなんだけど。P117-P118

4.5 テキストファイルの処理

GNU/Linux環境なら、デフォルトがUTF8なのでこれに悩むことはないが、他のエンコードをもつシステムとデータのやりとりをする必要があるなら読んでおこう。ネット上にも記事はあるが、せっかく買ってあるのだから読めば良い。P118-P121

4.5.1 大混乱なデフォルトエンコーディング

使っているOSで、どういう風にデフォルトエンコーディングになっているかの説明をしている。確認方法を提供しているので、混乱したらここから調査を開始するのもいいかと。P121-P124

4.6 適切な比較のためのUnicodeの正規化

ソートを含めて文字の大小比較をどうするかについて。アクセント記号の話題だったりするが、日本語でもまったく関係ないわけでもない。うに濁点とか。絵文字とか。は本書で取り上げていないけど関係ある。あと検索時にアクセント記号をつけていなくても付けているの同じように検索したいとかありそうな仕様ですし。日本語だとカタカナだろうがひらかなだろうが、同一視したいとか。と考えたらいいのかな。

本文例だと、1/2を3文字のASCIIで入力してもUnicodeの一文字1/2を同一視することを書いている。

監訳者注で、NFC,NFD,NFKC,NFKDについて略していない用語をだしている。これらの意味を知りたいならUnicode本が必要っぽい。この辺書いた本があれば後でここに書き足そう。文字コードの本は何冊かあるはず。オンラインで検索してもいいけども

P124-P127

4.6.1 ケースフォールディング

基本的には、すべての文字列を小文字に変換することなのか。例外もある。が本文を参照してほしい。p127

4.6.2 テキストを正規化してから比較するユーティリティ関数

NFCとNFDが出てくるが、p125 で用語の説明がしてある。

4.6.3 付加記号う取り除く極端な正規表現

著者としては、力のはいっている部分なんだろうと思う。でも日本語話者としては、もっと身近な例がいいな。

p128-o133

4.7 Unicodeテキストのソート

localeに応じてソート。GNU/Linuxなら充分に動くが、…

4.7.1 Unicode照合アルゴリズムでソート

ここで、PyUCAが出てくる。localeは見ずに、結果をカスタマイズすることができる。GNU/Linux以外の人には朗報だね。p135

4.8 Unicodeデータベース

正規表現については、いまでもPyPIの方が優れているのだろうか。新しいバージョンで更新されているのかとか確認する方法は、簡単に分かったりしないのだろうか。開発を追っかけるのが堅い手ではあるのだけども。

4.9 str/bytes デュアルモードAPI

4.9.1 正規表現におけるstrとbytes

具体例がでてくる。

CJKのってか、日本語の場合は??

4.9.2 OSモジュールにおけるstrとbytes

GNU/Linux上での説明に始まり、他のプラットホームはそこそこ。

この扱いをみていると、一番便利なのはGNU/Linux上でUTF8で使うことで、私はそのど真ん中の例なんだな。

4.10 本書のまとめ

これ理解するのは、本当に大変で、資料を読んで、自分の卑近て例だけでわかった気になるとヤバいやつだというのはわかった。GNU/Linux上で、Pythonを使っている幸せを感じてポーティングするより windowsとかでもwsl2もあることだし、そっちで何とかしてくれと言うのが良さそう。もうUTF8で幸せになろうよ。ってのが私の感想です。

5章 第一級関数

最初に、Guidoが、関数型言語から影響を受けたとは思っていないという引用から始まる。しかしこれを明言しない理由は何だったんだろうか。

2020-07-12 06:47

第一級関数=第一級オブジェクトの定義を読んでいて、気になることがある。

  • ランタイムに生成できる。
  • データ構造の中の変数や要素に代入できる
  • 関数の引数として渡すことができる
  • 関数の結果として返すことができる

で、最初のランタイム4に生成できる。というのに違和感がある。ランタイムは、実行バイナリのことを指してランタイムということがある。が、pythonの場合コンバイラとしてバイナリを吐き出すのが、いつでも成り立つ訳ではない。実行している時にとか、プログラムを実行している間にとか、そういう意味ではないのか。

関数が第一オブジェクトでない言語って、BASICとかかな。最近の言語だと可能なのが多いので分からんようになるわ。

5.1 関数をオブジェクトのように扱う

実例をだしている。

5.2 高階関数

ここでの高階関数の定義は、「関数を引数として受け取ったり、結果として関数を返したりする関数」とのこと。この定義は、ボケットリファレンスのp268にある 「16-1-3 高階関数を使う」においても同じ内容であった。

5.2.1 map,filter,reduceの最新の代替

もう、python2は考慮しなくてよいものとして読むスピードを上げる。

map,filter,reduceよりも、リスト内包表記やジェネレータ式で代替できるので、使われなくなった。というのは、なるほどと思った。パイソン風(pythonic)なプログラミングでは、そう書くのだと。例5-5でmapをリスト内包表記に書き換えている実例を出している。

例 5-6 で、reduceを使う典型例として、合計値を求めるのに、sumを使う例を出している。

mapとfiltgerは、ジェネレータを返すので、その扱いを変えるのが良いという話である。これも使う関数を真似るのではなく、使っている真意をマネよってことで。読んでよかったなと。

一つの値というか結果に「集約」するというのが、キモだということ。

あとの10.5や、14.11 でのイテラブルについても言及すると先触れを出していている。

ちなみに、ポケットリファレンスでは該当するページはp269-p271であるが、ここの部分の学びについては書いていないので、自分で追記しておく。

5.3 無名関数

lambdaの話。

いつ使えが明解でよい。

頑張りすぎて、わかりににくいlambdaを除去するリファクタリングレシピもいい。p154

5.4 7つの呼び出し可能オブジェクト

呼び出しオブジェクトについて、列挙し、コメントをして理解しやすくしてくれている。後のために、ジェネレータ関数の所で、コルーチンについて言及している。また呼び出し可能か判定するのは、そのオブジェクトのcallable()を呼んでみる。のを実例を入れて書いている。p154-p156

5.5 呼び出し可能なユーザ定義型

example-code/bingocall.py at master · fluentpython/example-code - インスタンス化する時にクラスを実行する - インスタンス化したオブジェクトを実行すると、 __call__(self):を定義してどんな戻り値にするか決めることができる。

疑問点
p157で書いてある、クラスに、 __call__ を実装すれば、の下りは、ここで何を言っているのか意味がよく分からない。他の人とも議論してみたい。

5.6 関数のイントロスペクション

5.10 関数型プログラミングのためのパッケージ

純粋な関数型プログラミングう行うというよりも、lru|cache などのデコレータを便利に使うための手段して関数型を取り込んでいるというのが、後から読み直した時の所感です。便利に使うためにはA事前に学んでおかないといけないので、後で効いてくる。

6章 第1級関数を使ったデザインバターン

see also オライリーの実践python3の方が、デザインパターンについて詳しいのであるが、本書では、Strategry のパターンについて繰り返しpythonの異なる機能を用いて紹介している。

6.1 リファクタリングのケーススタディとしての Strategy パターン

6.1.1 典型的な Strategy パターン

抽象基底クラス(ABC) として使うために、@abstractmethodデコレータを使っている。7章を読んだ後だと、やはりpythonは、デコレータの使いこなさないといけない。

6.1.2 関数指向の Strategy パターン

6.1.3 シンプルな方法による最良の Strategy の選択

気をつける点について、解決方法を下記で解決策を提示している。

6.1.4 モジュールにある Strategy を検索

  • globals()を使って関数を見つける
  • イントロスペクションを使う

詳細は、本書のp186-p187を参照せよ。

Command パターン

図6-2 p188のUML図

実行するコマンドのリストを生成する、より高度なundoを実装するためのヒントを提案してくれている。 __call__ メソッドをわかってからここにくる必要がある。

6.3 本章のまとめ

Design Pattern は、金科玉条でなく、言語の特性を応じて、コードの書き方でなくアイデアを活かして実装する話である。なので、他言語で慣れていても、pythonらしい書き方をする。(Fluent python)にあてはめ、こういう風にするのだ。ということを著者は言いたかったのだろうな。と思いながら読んだ。

6.4 参考文献

Soapboxで、7章に、Visitorパターンを作るときに、有用であるという、ジェネリック関数を予告している。また、CLOSのマルチメソッド5など予告しており、脳内の関連情報を読者に伝えたい意図を感じた。

7章 関数デコレータとクロージャ

デコレータを使いこなすためには、クロージャの理解が必要とのこと。

p194にある。

  • Pythonのデコレータ構文の評価方法
  • 変数がローカルであるかを Python が判断する方法
  • クロージャの存在意義とその挙動 nonlocalで解決できる問題

上記の説明をこの章で受けて、クロージャの挙動がよくわかったので、とても良かった。python ポケットリファレンスでは、端的に説明されていて、理解できていなかった。これは、ボケットリファレンスなので、この様な概念をキチンと説明するのは荷が重いのは理解するが、重要な概念だけに省くことはできなかったのだろうな。とも思う。プログラミング経験者が、pythonの教科書を購入するなら、ここは一つ内容を確認しておくのがいいのではないか。と私は思う。もちろん、もう理解しているから要らないと意見もあるかもしれない。

コーディングを支える技術
別の本、「コーディングを支える技術」で、オブジェクト指向の話がでてくる。そこで、「11.2 変数と関数を束ねて模型を作る方法」p190がある。ある目的を達成するために、3つの方法がある。と書いている。(1)モジュール — Perlで使われている関数を束ねておくパッケージと、変数を束ねておくためのハッシュを結びつけておく方法(2) 関数も変数もハッシュに入れてしまう。JavaScriptなどが採用している方法(3)クロージャという主に関数型言語で使われている関数実行時の名前空間を変数をまとめるために使う方法です。と書いている。それぞれ詳しくは、本を参照して欲しいが、クロージャの説明として「11.5 方法3:クロージャ」P206があるが、実は、その前の「11.4 方法2:関数もハッシュに入れる」も読んでおかないと、fluent pythonで書いてあることに関係するからです。「ファーストクラス」「関数をハッシュに入れる」など。ここのクロージャの説明は、javascriptを例にしているので、pythonを学ぶ目的であれば、fluent pythonの方が良いだろう。「自由変数」というキーワードは「コーディングを支える技術」でも、でてきた。クロージャがなぜ「閉じる」という話になるのか。という概念になるのか。

7.1 デコレータの基礎

どういう動作をするか。についてのチュートリアル。

7.2 デコレータ実行のタイミング

ここのキモは、デコレータの実行タイミングを書いており、インポート時に何が起きているのか。を書いている所です。たしかに、そういう風に動くにはこのタイミングか。というのは理解できる。

7.3 デコレータを使った Strategy パターンの改善

例7-3は、example-code/strategy_best4.py at master · fluentpython/example-code が該当する

動いているコードで、理解を深めるのがいい。

7.4 変数スコープ

ここで、pythonでのスコープを確認する。

例7-4, 例7-5 は、example-code/global_x_local.rst at master · fluentpython/example-code が該当するようだ。

ローカル変数を、globalとして扱うための説明で、nonlocalの説明の前フリである。

7.5 クロージャ

クロージャは、理解されにくいのか。違うモノと混同されている。

図 7-1 ここの自由変数の図を読もう。

これ、例7-11 例7-12など

7.6 nonlocal宣言

例 7-13 例 7-14 も既存か。

ここの説明が、python3になって、nonlocalが導入された説明になる。python2においてのやり方が書いている。p206

7.7 シンプルなデコレータの実装

7.7.1 コードの解説

__name__ と __doc__ を覆い隠さないバージョン で、なんでこうなるかについても説明している。

functoolsのなかにある functools.wraps は、functools.wraps: functools — 高階関数と呼び出し可能オブジェクトの操作 — Python 3.8.5 ドキュメント であり、functools.update_wraps functools.update_wrapper: functools — 高階関数と呼び出し可能オブジェクトの操作 — Python 3.8.5 ドキュメント を呼んでいるので、こっちも見る必要がある。

7.8 標準ライブラリのデコレータ

property, classmethod, staticmethodの説明は後にするとのこと。functools.wraps は、既に紹介した。lru_cacheとsingledispatchの説明をするとのこと。

7.8.1 functools.lru_cache を用いたメモ化

メモ化
ここのnoteの主旨からは外れるかも知れないが、lru_cache を「メモ化」という表現は、pythonにおける定訳だと気がつくまで、かなり違和感があった。 lru_cacheの概念を「メモ化」という概念で日本語として互換だと消化するまでに時間がかかったからです。記録してすぐに参照するというコアなアイディアを共有すると良かったのだと今にしては思う。納得しがたかったのは、自分が、lru_cacheの動作の方から考えていたからなのだろう。

ここでのコア・アイディアは、一回実行した結果をlru方式で記録しておく。その時の条件として、入力が一定なら、必ず同じ出力をするのであれば再利用が可能だから。というもの。なので引数と出力をメモっておく。ただ気をつけないといけないのは、プログラム内部で、乱数や時刻などを参照していると同じ結果にならないので、何でもかんでもメモ化していいという話にはならない。

また、キャッシュをシステムで多重に持つと副作用があるので、どのレイヤで何のキャッシュを持っているのか。を設計時に決めておかないといけない気がする。そのためには、最初からパフォーマンス計測を計画して、プログラムに組み込んでおく用心深さがいるのかもしれない。

当てずっぽでなく、データを収集する。は、話がfluent pythonから脱線したが、デコレータで気軽につかえるlru_cacheは、プログラミングテクニックとして、覚えておくのは良い。ちょっとずつ違うことを繰り返し参照する場面では強力な手助けになるだろう。

7.8.2 シングルディスパッチのジェネリック関数

パッと見には、JavaやC++のオーバーロードと同等に感じるかも知れないが、p215で書かれている内容を、いまは理解できていない。得失点まで含めて人に説明できるようにならないと理解したとは言えないだろうし。

ここでの、抽象基底クラス(ABC)推しも、体感してみるのと、その便利さは何が代償なのかを確認しないとよくわからない。コードの拡張性とのトレードオフは、実行速度だったりすることもある。でも、pythonで書いている時点で、ある程度割り切りはできているはずで、その基準内であれば拡張性を優先するのはありえる話です。

7.9 多重デコレータ

デコレータを複数回実行すると何が起きているのかを記述している。p214

pythonポケットリファレンスのp273 16-1-7 デコレータ構文を利用する

の記述では、よくわからない挙動の確認に。ここと次の部分は有用だと感じた。

「実践Python3」の 2.4.2 クラスデコレータ p59 に、多重デコレータの適用順がある。クラスが生成されてから、下から上に向かって順に適用されていく。との記述あり。クラスデコレータと、関数デコレータの評価順を一応確認しておくこと。2020/07/31

7.10 パラメータ化デコレータ

p216 の本節の最初の行を、なぜにpythonポケットリファレンスは、書いていないのか。経験を積むとわかるのだろうか。でも紙面が少ないから削られたのか。わからないけどもここは、自分で書き込みをして補っておく場面だよな。

いや、本に書き込むスペースぐらいでは足りないので、書き付けた紙を挟んでおくと、まだまだ書けるぞ。

7.10.1 登録デコレータのパラメータ化

p217 ここで、デコレータファクトリの説明をしている。

7.10.2 clockデコレータのパラメータ化

7.11 本章のまとめ

メタプログラミングの入り口かぁ。

7.12 参考文献

Soapbox の「自由変数」に関する記述が気になった。

あと、pythonのデコレータと、デザインパターンのDecorator

8章 オブジェクト参照、可変性、リサイクル

8章のテーマは、オブジェクトと、オブジェクトにつけられた「名前」の区別をする。という話とのこと。

一時期よく見かけた、変数は箱のメタファーは間違っている。という話をpythonからの視点で説明する。

  • オブジェクトの同一性(identity)
  • 値(value)
  • エイリアス(alias)

の概念を説明し、shallow copyとdeep copy を説明する。

8.1 変数 != 箱

確保されたメモリ領域へのラベルという解釈をした。

8.2 同一性、等価性、エイリアス

例 8-3 は、とくに簡単なのでサンプルプログラムはない模様。同じオブジェクトを別の名前が、指している例

aliasと、同じ値のオブジェクトを持っているだけでは、意味が違う。この意味は、誤解しているとバグう生みそう。

p233の、Python言語リファレンスの引用、 3. データモデル — Python 3.8.5 ドキュメント は、fluent pythonの訳と、有志で日本語にしている訳では、表現は異なっていますが、大意は同じものだと思うので、左記にlinkをはっておきました。

内容が同じでも、オブジェクトのID6 は異なる。実装依存であっても、形式と意味する内容が決められているという部分が大事で、オブジェクトが存在している間は不変だということがキモっぽい。

8.2.1 == と is の使い分け

この部分、pythonポケットリファレンスだと、さすがに記述はあってもどういう風に使うのかを理解できるようになるのを期待したい。

典型例として、

x is None
x is not None

があるので、Noneかどうかを高速に比較したい。という、検査目的を感じた。

ダンダーとして、 __eq__ の話に及んでいるので、2つのオブジェクトが、中身を検査するのが、コストが高いというのは理解できたと思う。shallow copyやdeep copyへのネタ振りでもあろう。

8.2.2 タプルの相対的な不変性

ここのタプルが不変だという話を理解しておかないと、参照先では変化しうるけど、タプルなんで、そんなことない。って思い込みそうなので、読んでおきましょう。

8.3 デフォルトのコピーは「浅い」

ここの実験も、見ているだけだと、そうやな。って思うだけ。で、期待しない動きになってから、ここを読み返すことになる。

8.3.1 任意のオブジェクトの「深い」コピーと「浅い」コピー

deep copy と shallow copyの話。

例 8-8 生徒を乗せたり降ろしたりするバス

のコード例は、

example-code/bus.py at master · fluentpython/example-code

上記のコードを使って

  • copy
  • deepcopy

の実例

8.4 参照としての関数の引数

pythonは、共有渡し(call by sharing)だけということだが、c言語をやってきた自分には、call by valueとcall by referenceという言い方が馴染んでいる。

8.4.1 引数のデフォルト値に可変型を使うのは考えもの

問題があることを、例 8-12 可変なデフォルト値の危険性を説明する簡単なクラス で説明している。生徒の幽霊がでるお化けバス haunted bugってことみたい。

http://pythontutor.com/live.html#code=%22%22%22%0A%3E%3E%3E%20bus1%20%3D%20HauntedBus%28%5B'Alice',%20'Bill'%5D%29%0A%3E%3E%3E%20bus1.passengers%0A%5B'Alice',%20'Bill'%5D%0A%3E%3E%3E%20bus1.pick%28'Charlie'%29%0A%3E%3E%3E%20bus1.drop%28'Alice'%29%0A%3E%3E%3E%20bus1.passengers%0A%5B'Bill',%20'Charlie'%5D%0A%3E%3E%3E%20bus2%20%3D%20HauntedBus%28%29%0A%3E%3E%3E%20bus2.pick%28'Carrie'%29%0A%3E%3E%3E%20bus2.passengers%0A%5B'Carrie'%5D%0A%3E%3E%3E%20bus3%20%3D%20HauntedBus%28%29%0A%3E%3E%3E%20bus3.passengers%0A%5B'Carrie'%5D%0A%3E%3E%3E%20bus3.pick%28'Dave'%29%0A%3E%3E%3E%20bus2.passengers%0A%5B'Carrie',%20'Dave'%5D%0A%3E%3E%3E%20bus2.passengers%20is%20bus3.passengers%0ATrue%0A%3E%3E%3E%20bus1.passengers%0A%5B'Bill',%20'Charlie'%5D%0A%0A%0A%3E%3E%3E%20dir%28HauntedBus.__init__%29%20%20%23%20doctest%3A%20%2BELLIPSIS%0A%5B'__annotations__',%20'__call__',%20...,%20'__defaults__',%20...%5D%0A%3E%3E%3E%20HauntedBus.__init__.__defaults__%0A%28%5B'Carrie',%20'Dave'%5D,%29%0A%3E%3E%3E%20HauntedBus.__init__.__defaults__%5B0%5D%20is%20bus2.passengers%0ATrue%0A%0A%22%22%22%0A%0A%23%20BEGIN%20HAUNTED_BUS_CLASS%0Aclass%20HauntedBus%3A%0A%20%20%20%20%22%22%22A%20bus%20model%20haunted%20by%20ghost%20passengers%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20passengers%3D%5B%5D%29%3A%20%20%23%20%3C1%3E%0A%20%20%20%20%20%20%20%20self.passengers%20%3D%20passengers%20%20%23%20%3C2%3E%0A%0A%20%20%20%20def%20pick%28self,%20name%29%3A%0A%20%20%20%20%20%20%20%20self.passengers.append%28name%29%20%20%23%20%3C3%3E%0A%0A%20%20%20%20def%20drop%28self,%20name%29%3A%0A%20%20%20%20%20%20%20%20self.passengers.remove%28name%29%0A%23%20END%20HAUNTED_BUS_CLASS%0Abus1%20%3D%20HauntedBus%28%5B'Alice',%20'Bill'%5D%29%0Abus1.pick%28'Charlie'%29%0Abus1.drop%28'Alice'%29%0Abus2%20%3D%20HauntedBus%28%29%0Abus2.pick%28'Carrie'%29%0Abus3%20%3D%20HauntedBus%28%29%0Abus3.pick%28'Alice'%29%0Abus2.pick%28'Bill'%29%0Abus2.drop%28'Carrie'%29%0Abus3.pick%28'Carrie'%29%0A&cumulative=false&curInstr=42&heapPrimitives=nevernest&mode=display&origin=opt-live.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false

で、実行してみて解ったことがある。

8.4.2 可変な引数を使うプログラムを頑強に

例 8-15 引数変更の危険性を示す簡単なクラスとして下記の例がでている。

passengerのチェックをすることで、独立というか、別物にできる。ここは、また訪れて確認しないとわかった気にならないな。

http://pythontutor.com/live.html#code=%22%22%22%0A%3E%3E%3E%20basketball_team%20%3D%20%5B'Sue',%20'Tina',%20'Maya',%20'Diana',%20'Pat'%5D%0A%3E%3E%3E%20bus%20%3D%20TwilightBus%28basketball_team%29%0A%3E%3E%3E%20bus.drop%28'Tina'%29%0A%3E%3E%3E%20bus.drop%28'Pat'%29%0A%3E%3E%3E%20basketball_team%0A%5B'Sue',%20'Maya',%20'Diana'%5D%0A%22%22%22%0A%0A%23%20BEGIN%20TWILIGHT_BUS_CLASS%0Aclass%20TwilightBus%3A%0A%20%20%20%20%22%22%22A%20bus%20model%20that%20makes%20passengers%20vanish%22%22%22%0A%0A%20%20%20%20def%20__init__%28self,%20passengers%3DNone%29%3A%0A%20%20%20%20%20%20%20%20if%20passengers%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.passengers%20%3D%20%5B%5D%20%20%23%20%3C1%3E%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.passengers%20%3D%20passengers%20%20%23%3C2%3E%0A%0A%20%20%20%20def%20pick%28self,%20name%29%3A%0A%20%20%20%20%20%20%20%20self.passengers.append%28name%29%0A%0A%20%20%20%20def%20drop%28self,%20name%29%3A%0A%20%20%20%20%20%20%20%20self.passengers.remove%28name%29%20%20%23%20%3C3%3E%0A%23%20END%20TWILIGHT_BUS_CLASS%0Abasketball_team%20%3D%20%5B'Sue',%20'Tina',%20'Maya',%20'Diana',%20'Pat'%5D%0Abus%20%3D%20TwilightBus%28basketball_team%29%0Abus1%20%3D%20TwilightBus%28basketball_team%29%0Abus2%20%3D%20TwilightBus%28basketball_team%29%0Abus1.drop%28%22Tina%22%29%0Abus2.drop%28%22Pat%22%29&cumulative=false&curInstr=26&heapPrimitives=nevernest&mode=display&origin=opt-live.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false

確保したオブジェクトを使いまわすのは、C言語の、call by referenceと同じように思ってしまうが、本章のSoapboxの部分もあるので、また読み直して理解に努めたい。

なお、前節 「8.4.1 可変な引数を使うプログラムを頑強に」と異なるのは、可変な引数の内容が、[]な時と、そうでない時に、同じオブジェクトを指していないことが確認できる。前者と、初期値を[]かどうかで振り分けている本節のプログラムの差については、理解した気がするが。 see also 本章のSoapbox

8.5 delとガベージコレクション

delは、名前を削除し、ガベージコレクションは、オブジェクトを削除する。CPythonのガベージコレクションは、reference counterを使っているとのこと。ガベージコレクションはそれだけで、一つの学ぶべき分野であるが、fluent python p245 ではreference cunterの説明がなされている。

weakref.finalize ってので、オブジェクトが削除されたときに実行されるコールバックを設定できることを知る。

名前を削除すると、到達性がなくなり、ガベージコレクションを動くという連鎖が理解できたらいいのかな。

CPython 2.0 は、世代別ガベージコレクターとあるが、脚注に、監訳者からの説明あり。 p245

8.6 弱参照

オブジェクトで、置いておく必要がないもの。本書では、cacheが例に上がってきている。を積極的にメモリーへ返却したい時にどうするか。という話と、こんなん事前に調べとかないとわかる訳ないやん。的なtipsもあって、p247 の部分はまた再訪します。

8.6.1 コント「Weak Value Dictionary」

解説を読んでも、疑問だったので、globalの定義を読み直さないと間違えそう。他の言語のフワッとした感覚だといかんな。pythonポケットリファレンス p183にて確認する。

私の理解によるスコープは、2つの条件で決定される。

  1. 使用する場所(宣言する、確保すると考えてもいい) #A
  2. 代入する場所 #B

#Aが関数外で宣言というか確保されていると、グローバル変数となり、関数宣言をしてから、グローバルと同じ関数名を使って代入をすると、別途ローカル変数として扱われる。グローバル変数にアクセスしたいなら、global宣言が必要。というか別の名前にするという手で問題にならないのか。古き変数名に属性を持たせるという方式だったら困らないってことか。

また、グローバル変数は行儀がよくないから、クラスの中に閉じ込めるクラス変数などに閉じ込めるので、ここを意識してこなっかった。

クロージャの場合は、関数宣言をして、そのまた中から、外の関数で定義された自由変数にアクセスする。ただ変数を見たいというだけなら、再代入を伴わないことはマレなので、やっばりnonlocal宣言は必要か。

#B の再代入というのがキモな気がする。その時にどの変数にアクセスしたいのかを明確に意識しそう。

あと、WeakSetの説明は、リファレンスで書いていない、注意すべきことを書いているので読んでおけ。p250

8.6.2 弱参照の制約

dictやListについての制約があること、そしてその制約を外す方法 p250

8.7 Pythonが不変型で使っている魔術

この節は、飛ばしていいとのことで、何周かして余裕があったら読む。

8.8 本章のまとめ

ここの部分を読んで、内容の確認をする。

8.9 参考文献

Soapbox

また、Soapboxの「共有渡しによる引数の受け渡し」を読んで、単純に、call by reference という言い方ではいかんというのがやはり理解できていない。p256

Pythonの関数は引数のコピーを取得しますが、引数は常に参照です。 そのため、参照されているオブジェクトの値は可変であれば変更されることもありますが、そのIDは変更できません。 また、関数は引数で指定されている参照のコピーを取得するので、再バインドしても関数の外側には影響を与えません。

については、

Pythonの関数は引数のコピーを取得しますが、引数は常に参照です。

多分理解できたとオ思う。

そのため、参照されているオブジェクトの値は可変であれば変更されることもありますが、そのIDは変更できません。

前段は、そうだと思うが、内容が変わったら、IDが変化するんじゃないのか。IDが変更できないということは、内容を最終的に変わらないということを意味しているのか。

また、関数は引数で指定されている参照のコピーを取得するので、再バインドしても関数の外側には影響を与えません。

参照のコピーといっても、shallow copyなので、同じオブジェクトを参照しているのではないのか。そして「再バインド」という言葉が出てくるが、何となくの意味でなく、pythonにおける変数の再バインドとは、何か。再代入のことなのか?

という疑問があるので、やっばり理解しているとは思えない。また、わかったら追記する。

2020/08/01
idは変わらない。というのは、下記のようだ。
>>> a = ["aaa","bbb","ccc"]
>>> def fna(arga):
...     b = arga
...     b.append("ddd")
...     print(b)
... 
>>> def fnb(argb):
...     c = argb
...     c.append("eee")
...     print(c)
... 
>>> print(a)
['aaa', 'bbb', 'ccc']
>>> fna(a)
['aaa', 'bbb', 'ccc', 'ddd']
>>> fnb(a)
['aaa', 'bbb', 'ccc', 'ddd', 'eee']
>>> print(a)
['aaa', 'bbb', 'ccc', 'ddd', 'eee']
>>> id(a)
140204036553472
>>> fna(a)
['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'ddd']
>>> id(a)
140204036553472
>>> fnb(a)
['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'ddd', 'eee']
>>> id(a)
140204036553472
>>> print(a)
['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'ddd', 'eee']

オブジェクトのIDは不変でも、内容は変わっていくってことは、こういうことか。

9章 Pythonic なオブジェクト

ここでは、ユーザ定義型を作るテクニックについて学ぶようだ。p259

この章で学ぶのは、

  • repr()やbytes()など、オブジェクトを異なる表現で示す各種の組み込み関数を使えるようにする方法
  • 別バージョンのコンストラクタをクラスメソッドとして実装する方法
  • 組み込みメソッドのformat()や、str.format()メソッドが用いる書式指定ミニ言語の拡張方法
  • 読み取り専用属性へのアクセス
  • セットやdictのキーとして、利用するためのハッシュ可能オブジェクト
  • __slots__ によるメモリの節約

と、下記の議論をするようだ。

  • @classmethodおよび@staticmethodデコレータをいつ、どのように使用すべきか。
  • Pythonのプライベート属性とプロテクト属性の用法、慣例そして制約

どういう関連でこれを本章に入れているのかは、最初の段階ではよくわからない。読み進めるとおいおいとわかるのだろう。

9.1 オブジェクトの表現

  • repr() — __repr__
  • str() — __str__

の説明をしたあとに、

  • __bytes__
  • __format__

をする模様だ。

ってことで、この章もダンダーメソッドについての説明であるようだ。

9.2 Vectorクラス再訪

例 9-2

typecodeについては、fluent python p52 または、array — 効率のよい数値アレイ — Python 3.8.5 ドキュメント にある「型コード」を参照せよ。fluent pythonと、公式ドキュメントの用語が違うのは、訳者が違ったり、翻訳の用語リスト、翻訳メモリが違うのだろう。読者側で、変換して吸収するしかない。

9.3 別バージョンのコンストラクタ

classmethodについては、まずは気にせずに読み進めて戻るのがいいってこと。?

とか、見ても、もっと端的に説明してもらってから詳細に入ってくれないか。とか思う。

クラスに属している、メソッドをインスタンス経由でなく、「クラス名.メソッド名」という形でアクセスして実行する形になるのかとか思うが、

Effective python 「項目 24:@classmethodポリモルフィズムを使って、オブジェクトをジェネリックに構築する」p64-p68 とりわけ、p67 での、 インスタンスメソッドポリモルフィズムと、@classmthodポリモルフィズムの対比の記述が参考になるか。

classmethodデコレータの説明は、次節で説明してもらえるようだ。

9.4 classmethodとstaticmethod

p264

インスタンスでなく、クラスに対する操作を行うメソッドを定義するときに用います。

のこの周辺を読むのと、変数名の慣習について言及がある。あと、クラスに対するは、クラスに属する操作とも読めるか。クラスをインスタンス化しなくもいいというのは、そういう意味にも取れるか。

staticmethodは、classmethodとの対比で出てくる。対称性として、存在しているとなると、どう使うのか。ここに書いてある方法だけなのか。例外はあるかとか思うが、そんなことは、後からでいいか。

9.5 出力フォーマット

p265 実際にデリゲートされるメソッドについて書いてある。デリゲートってか実際に動作する部分と書いておくのがいいか。

自分の作ったクラスで、書式指定ミニ言語仕様に沿った、出力をさせたいときにどうするか。を示している。

9.6 ハッシュ可能なVector2d

自分の作ったクラスを、Setなどに入れて使いたいなら、ハッシュ可能にする必要がある。そして、p269-274は、その方法についてプログラムを交えて説明している。

上記で、実装されているダンダーたち、読み取り専用プロパティにする。@propertyデコレータの詳細は19章になるとのこと。19章に至ったら戻ってくるか。

そして、 __hash__ として、どういう値が良いのかの例を出している。必ずしも必須ではないが、事実上readonlyなプロバティにする必要があったのもここで説明される。p271

9.7 プライベート属性と「プロテクト」属性

プライベート属性がどうやって実装されているのかを明らかにして、Pythonプログラマは自分で自分の足を撃ち抜ける自由もある。ので、正気なプログラマに便利な方法を提供している。

9.8 クラス属性 __slots__ によるメモリ節約

ここの記述を使うハメになるのなら、NumPyやPandasを検討しようというのは良い。

9.8.1 __slots__ の問題点

p280 使うなら読んどけ。

9.9 クラス属性の上書き

クラス名で動作を変えたい時に役立つ。

9.10 本章のまとめ

9.11 参考文献

Soapbox

10章 シーケンスをばらして、ハッシュして、スライスする

ダックタイピングについてより考察を深める。一章で出てきた、 __len__、 __getitem__ をより詳細などなど。「非形式なインターフェースとしてのプロトコル」という話題が出てくるようだが、概念と具象をつなげる部分に興味がある。

コラムの「多次元ベクトルアプリケーション」は良い。意欲が出たが、一気に全部はできないので、まずは本書を一通りやってから。

10.1 ユーザ定義のシーケンス型(Vector)

継承でなく、compositionで実装するのはなぜか。

10.2 Vector2d互換(Vectorテイク1)

例10-1で、コンストラクタがこっちがベストプラクティスってことで。

質問がでていた、reprとstrの違いについては、p291のサソリのマーク部分で、その答えがある。

10.3 プロトコルとダックタイピング

ずっと気になっていた、プロトコルの説明が、本節である。fluentpythonで述べられている、プロトコルの定義って、他でも使われているのか?

オブジェクト指向プログラミングにおけるプロトコルは非形式的なインターフェースで、コードではなくドキュメントでのみ定義されるものです。

という、文章は次の行から具体例を示しているが、この様な形式でコードを実装せよ。という例が書いてあり、「非形式的なインターフェース」って何だ?と、「コードでなくドキュメント」ってのは何を指しているのか、私に取っては自明でないが、読み進めていくと理解できるといいな。

他のOOPをする言語だと、確かにシーケンス型を継承しないと、そのような動作にならない。という前提で、そのようなプログラミング言語に対して、pythonはダックタイピングで、ダンダーメソッドを整備すれば、同じモノとして見なす。という意味なんだろう。と理解しておく。

そして、fluentpythonにおいてダックタイピングの定義っぽいものは、p294にある。

10.4 スライス可能てシーケンス(Vectorテイク2)

シーケンスプロトコルの処理を作るのに、これから記述していく。

10.4.1 スライスの仕組み

10.4.2 スライス対応版の__getitem__

10.5 動的な属性アクセス(Vectorテイク3)

__getattr__ の紹介と使い方。

p299 ここで、求める属性がどういう風に検索されているか。を書いてくれている。

  1. インスタンスに名前があるか
  2. クラスの中を検索
  3. __getattr__

で探すとあり、もうちょっと詳細に書いてある。

10.6 ハッシュとより高速な==(Vectorテイク4)

__hash__ と __eq__ の実装。そして、functools.reduceを使う。 lambda を使って、5!(5の階乗)を計算する方法については、p303にあり、reduceの基本的な方法を学ぶことができるが、次ページのp304にて、xorを取る3つの方法が例示されている。特に、(3)は、作者のお気に入りであるが、これは標準で付いてくるライブラリを知らないと、ついつい知っている(1)の方法に頼りがちなので、ここもfluent pythonを読んでて良かったなと。

__eq__ と、 __hash__ は、近接さておくというのは、たしかに。(理由は、p305を参照せよ)

p305は、map - reduce 演算の説明、この概念を知らない人には、必要な説明だろう。また、p306-307にかけて、zipについて紹介している。map - reduce系の処理をするなら、とても使い勝手ので、自分の道具箱に入れておけというのは、よくわかる。python ポケットリファレンスだとp94です。

allの使い方も例示があり、この手の書き方に慣れていないなら、参考になる部分だと思う。

10.7 フォーマット(Vectorテイク5)

出力フォーマットを極座標系から、球面座標系へ変換するにあたって、コードを書き換える。既存の書式を書き換えないで拡張する方法について。

10.8 本章のまとめ

やった内容の振り返り

10.9 参考文献

ここで、最初に読んで良かったとおもうのは、p317の下記です。

とても使い勝手のよい reduceは、高階関数で、これをfold, accumulate, aggregate, compress, inject という名でも呼ばれています。詳細は、英語版Wikipediaの「Fold(high-order fucntion)」を参照してください。

でした。同じような概念を別の名前で呼んでいるなと、薄々は気がついていましたが、覚える概念が減ってありがたい。

Soapbox

ここの章のSoapboxは、私に取って収穫が多い。

非形式的なインターフェースとしてのプロトコル

ここに来て、ようやく「非形式的なインターフェースとしてのプロトコル」という内容を補完する記述がでてくる。まずは読み進めてみないとわからない評価はできないな。P317

プロトコルとインターフェースは、11章の主題とのことです。11章の冒頭でも書かれるでしょうけど。

ダックタイピングの起源

確かに本筋でないが、書いておきたいのはわかる。

ユーザビリティを向上した安全な __format__

ユーザの要求と、困りそうなことを考えてのコードを書くには。

PythonicなSumの探求

著者による、Pythonicとは何かについての意見の開陳と、pyhthonにいる自分の流儀が良いという流派の話と、pyhonicなコードとはいう話は直接は関係なくてもジワジワ効いてくる話だと思う。p319-p321

11章 インタフェース — プロトコルから抽象基底クラスへ

fluent pythonの索引で、インターフェースを探すと、p3を指し示しているが、本来は11章のここか、用語集でフォローアップが欲しい所か。本の1回目の感想と、1回通して読んだ後に評価を変えるであろう。 2020-08-02 17:32

  • インターフェース
    • 動的プロトコル
    • インターフェースを明確にして実装の適合性を検証する抽象基底クラス(Abstract Base Class:ABC)
      • これ実装の適合性とは?
      • 実装の適合性を検証するとは?どういう風に?

という、話と、コンパイラーを使う型をガンガン使う言語や、Javaなどの言語によっては、ダックタイピングという概念は目新しいという表現はよくわかる。Rubyを齧ったことで、ダックタイピングの一部を感じたことはあるが、便利な反面単体テストというか、ユニットテストで振る舞いを定義しておかないと心細くなる感じについては、別の議論になるだろう。

そして、ダックタイピングを別の言語で経験した人は、本当にダックタイピングを理解していたか、を確認した後に、「抽象基底クラス」と「型検査」を学ぶことになると、予告している。

著者は、抽象基底クラスを使いこなすことが、Pythonらしいプログラミング方法であると考えているか、抽象基底クラスを理解しないと次に進めないと考えているのだろうな。とこの時点では想像できる。そしてやり過ぎは良くないと釘を刺されるのであるが。(苦笑) フレームワーク作成のツールなので、全員が充分に使いこなせる必要はないようだ。でも、いつかは最初の一歩を踏み出さないといけない。

ちなみに、索引には「抽象基底クラス」はありません。用語集の方にありますが、索引は用語集を指していないのです。

11.1 Python文化におけるインターフェースとプロトコル

ここで、1章からここまで、ずっと「ダックタイピング」と、「プロトコル」について説明してきたと告白している。最初に地図が欲しかったよ。書いてある内容は、Pythonのデータモデルについてのこれまでの歩みをおさらいする感じ。p324

プロトコルの定義については、p325も書いてあるし、意味もわかるし、継承との区別も書いている。

同じことを指す別の言い方、

「X風オブジェクト」、「Xプロトコル」、「Xインターフェース」という言い回しは、Pythonistaにとっては同じものを強調するためです。

まじか。でも、そういう文化圏なら慣れるしかない。

11.2とことんまでシーケンスを追いかける

図11-1 は過不足なく読み方の説明が書いてあると思う。

  • 足りないダンダーメソッドがあっても、それを埋めるようにPythonは動くという話、p326-p327
    • __getitem__ があれば、
      • __iter__ を実装していなくても、イテラブル
      • __contains__ を実装していなくても、in演算子を利用可能
  • これまで上記を説明していたのは? と、特別扱い。というダックタイピング — 言語がダックタイピングするという表現は新鮮だ。

11.3 プロトコルをランタイムで実装するモンキーパッチ

モンキーパッチに関しては用語集への誘導がある。この節では、足りない処理をクラスに動的に追加?注入?定義?して、動かなかったコンソールセッションを動くようにしている。動的に足りない処理をプログラムのクラスやオブジェクトというかインスタンスというべきなのか。難しいな。

コードに書く時とは、また違った柔軟性を著者は見せている。教条的でないのはいいが、答えが複数あると迷う人はいるだろうが、これもFluent Python風なのかも知れず。著者の味なのか、Python特有なのかはもっと調べてみないとわからない。

11.4 カモ目の分類の話

なんで、抽象基底クラスの話が、カモ目の話になるのかは、何かの言葉遊びだろうか。疑問は棚にあげて、読み進める。ゲストの寄稿文章を読む。

ぐわっ。生物学の分類の話をマクラにして、話が進んでいる。動物詳しくないけど、着地点にたどり着けるだろうか。

抽象基底クラスの優位性は、More Effective C++のitem 33を見よか。持ってないから、どうするかな。今すぐでなくても良いが、いずれ内容を確認したい。

isinstanceと、issubclassの型チェックで、Trueを返してもらうために、抽象基底クラスを使うということらしい。ただ、isinstanceを使いすぎるのは、「コードの悪臭」とまで書いてあるので、使い時は考えろという話は理解する。オブジェクトの型をif-elif-elifしたくなるのはダメと。

p334の脚注9は、strをどうしたいのか、現在のpythonは克服しているかはわからない。棚上げにしておく。って、例11-7のことなの?p335の例と説明は良い。

処理を試してみてダメだったら、違う方式に変えてみる。というのは良い。

最後にまた独自で拡張してまで使いたくなるのは注意との警告が。「なんでも釘に見えてしまう症候群」にヒドい目にあったんだろうな。

11.5 抽象基底クラスのサブクラス化

collections.MutableSequence を使って、抽象基底クラスの使い方を解説している。

中身を知っていたら、より効率的な実装で、オーバーライドすることも示唆されている。

11.6 標準ライブラリの抽象基底クラス

Python 2.6からってことは、いまのPython3では、標準ライブラリに入っているってことで、collections.abc の説明がある。との予告

collections.abcの抽象基底クラス

collections.abcと、Lib/abc.pyの話があって、ちゃんと事前に区別して混乱しないようにしている配慮良い。公式ドキュメントをざっくり見たときに関係性がよくわからなかったで。

それぞれのコレクションの説明と関係性を書いている。

11.6.2 抽象基底クラスの数の塔

「数の塔」という用語は、小学生から中学にかけて、数の概念を拡張してきたことを思い出せばいいようだ。ただ、監訳者注の「この内包関係を縦に積んだ所から」の下りが、塔と形容しているようだが、この例だと複素数が一番下になるという理解でいいのか。整数は離散なので、存在している数としては密度が下がるので、上という概念なのかな。と想像してから、p340へページをめくるのがいいのでは。答え合わせできるから。

仮想サブクラスの関係についての注釈もあるので、数を扱う抽象基底クラスを扱うなら読んでおこう。

11.7 抽象基底クラスの定義と利用

抽象基底クラスを作る時はどんな時か、というのを例示している。

LookupError例外を投げるのが、良いというのは、戻り値でなく、例外で戻りう制御する方式か。扱う例外が少なくて、妥当な時には良い。ってか、乱立してたらその時点でヤバいってことか。 例外のクラス階層については、p344にある。またなぜTypeErrorが妥当なのかについても記述があるので、気になるなら読んでおくのが良い。

11.7.1 抽象基底クラスの構文

この文章を書いている時点では、python 3.7とか3.8が出ているので、ここに書いてあるabc.ABCMeta の話は読み飛ばしていいと思う。そして、過去の互換を取るための構文は、新しい @abstractmethod がデコレータを重ねることができるようになったので、覚えておく必要は少なくなった。p346 に説明がある。注意点であるが、これはLinterあたりが指摘してほしい感じ。

11.7.2 Tombolaのサブクラス化

11.7.3 Tombolaの仮想サブクラス

最初にこの部分を読んだ時には、実装が十分かどうかは、プログラマが確認するのは勿論だが、テストで例外が上がってこないことも確認しないと、間違えそう。というものだ。

regesterメソッドについて、p349-p351に説明がある。またPythonの継承関係は、__mro__ というクラス属性によって決定されることを書いている。これは、「コーディングを支える技術」の深さ優先と、C3線形化p227に関連する部分か。 Pythonがメソッドの検索をする順番を保持するリスト

11.8 Tombolaサブクラスのテスト方法

11.9 よくあるregisterの使い方

デコレータでない、registerの使い方。通常の関数として呼び出す形の使い方。面倒くさいのとちゃうの? いや手間は変わらんよ。という話

11.10 ガチョウもアヒルのように振る舞う

11.11 本章のまとめ

最初の1回だけで、理解できたとは到底思えない。そしてこんなコードを書かないといけないことになるのは、少ないのでいつまで覚えておけるのか心配だが、必要になったら躊躇なく使えるようにするために先ずは1回読み通した。足りないピースを填めてる感じ。

11.12 参考文献

Soapbox

型ヒント

この時の時点から、現在との差分は?

Pythonは弱い型付けか

用語と、相手の言う内容とが人によって違うと混乱する。ここの議論は必要になってから読めばいいや。

モンキーパッチ

Java、Go、Rubyのインターフェース

それぞれの言語においての、「インターフェース」という概念について。通信プロトコルやハードウェアのプロトコルも、インターフェースというので、解釈の余地が多くて何とかして欲しい。と思いながら読んだ。

インターフェースのメタファと慣用的用法

メタファは重要だが、拘りすぎるなって。判っていることからヤレという理解でいいの?

12章 継承の功罪

この章で学ぶのは、「継承」と「サブクラス化」を説明するとのこと。前章の11章で、サブクラス化するという表現を多用していた。その意味について、言葉を利用した後に詳細を説明していくスタイルなので、疑問に思ってもまずは読み進めるのが重要っぽい。

そして、索引と、用語集で、「サブクラス化」を探したが見つからず。「サブクラス」だと、索引で発見したが、p22とあるので、該当ページに「サブクラス」という言葉を発見できないのは私だけなのか? 読む価値がある本だと思うけど、元の本からしてこういう感じなのかしら。正誤表も検索しても、パッと見つからないので、まあ地道にやるか。この辺は誰の作業範囲なのか、奥付をジッと見る。

fluent pythonにおける「サブクラス化」
サブクラス化の意味は、現在対象としているクラスから派生して、自分で新しいクラスを作ることを指している。と思う。他にも「派生する」「継承する」としている技術文書もあり、fluent python内でどういう使い分けをしているのかは、まだ読み解けていない。そして単に、「派生する」「継承する」だと、継承の方向性の親子関係が分かりにくいから、「サブクラス化」という表現にしているのかも知れない。これも最後まで読み通さないとなんとも言えないので、最終的なジャッジをしたいから読みきりたいなと思う気持ちが沸いてきた。

fluent python 内容

  • オライリーの場合は、正誤表は本のページに属するので、ここに出てくるはず。

特に重点を置いているのは、次の2点のPython固有な話題です。

  • 組み込み型からサブクラス化するときの落とし穴
  • 多重継承とメソッド解決順序

の部分には、やってきた言語によって色々な意見があるだろう。本書では、多重継承を禁止しているJavaの影響(著者が、Javaの経験があるというのもあるだろう)が、 2020-08-04 の日本だと、もっと違う言語をやっている人も多かろう。また仕事で使っていなくても知見を広めるために学んでいることもある。とはいえ、Python固有の注意点がわかるのはありがたいことです。

具体例として、Tinkerと、Djangoで説明するとのこと。それぞれの最低限の知識は、中級プログラマとしては持っている前提だろう。少なくとも聞いたことはある。というような。(具体的にどのレベルまで要求されるかは、これから読むのですが。最初に本を読んだ印象と、着目点は2回目には得られないので、期待値とか、躓いたところは書いておきたい。)

12.1 組み込み型からのサブクラス化には注意が必要

ここの節の結論としては、listやdictをサブクラス化(派生/継承)したかったら、collection.UserDictを使え。

で、そうしないと、この節に書いてある不都合がおきる。詳細は、368p-370p

12.2 多重継承とメソッド解決順序

ここのキーワードは、「ひし形継承問題」です。これは、「コーディングを支える技術」の12.2 多重継承p218をシントピカル読書っぽく読みたい。なかなか「本を読む本」で定義されている問題意識う確認しながらやらないと脱線しそうだけども。

「コーディングを支える技術」のp219には、「実装の再利用に便利な多重継承」というページがある。pythonの、SocketServer.pyを例にあげている。同書の12.3 多重継承の問題点で、メソッドの検索について書いている。p221。そして名前が重複したら。どうすればいいのか。

同書では、3つの解決策が提示されている。

  1. 解決策1: 多重継承を禁止する。
    • 委譲(またはデリゲートとも)して処理をする。stategyなデザインパターンでやった。Pythonとしては、多重継承を許しているが、プログラミングする側で、使わないのは自由だ。—pythonicかどうかは、まだfluent pythonを読みきっていていないのでどれがいいとはわからない。この章を読みきったら分かっているといいな。「コーディングを支える技術」では、この部分で、「依存性の注入」(Dependency Injection:DI)に言及している。
    • Javaでのinterfaceについては、fluent pythonでもきっと言及があるだろう。
  2. 解決策2: メソッド解決順序を工夫する
    • 深さ優先探索法 p225
    • ひし形継承 p226の図12.12
    • C3線形化で順序を決める p227 python2.1までとC3線形化したpython2.3以降の違い。Perl6もDefaultの挙動に。p228
  3. 解決策3: 処理を混ぜ込む(Mix-in)
    • Mix-inについては、fluent pythonでもすでにやっているので、確認程度に。
    • Mix-inを使えばひし形継承を変形してより簡単な形に変形できる。p229
      • pythonの場合 p230
  4. 解決策4: トレイト
    • 今は直接はpythonに関係ないので、別途記述する。

というまで、ざっと読んでから、fluent pythonに戻ることにする。p371

  • 6. モジュール — Python 3.8.5 ドキュメント

    • import文, from xxx import aaa,bbb とかの確認に。
    • pythonボケットリファレンス import p192
      • モジュール名のみを自分のハッシュテーブルにいれる。ここを自分の使っている名前空間に入れるとすると間違いだとなるのだろうな。
    • pythonボケットリファレンス from xxx import aaa,bbb p198 「12-2 モジュールインポートのいろいろ」
      • モジュール全部でなく、指定した一部を、自分のハッシュテーブルに入れる。という表現も自分の名前空間に入れるのでアクセスできるという理解だと間違いになるのだろうか。用語の定義と使い方、理解のし方、説明の方法、難しい。名前空間とハッシュテーブルについての仕組みを分かっていると、スッと入ってくるだろうし。厳密に用語を使い分けることができるのか。
  • 例12.4

13章 演算子オーバーロードの適切な用法

ここは、必要な時に読めば良いと思った。この演算子オーバーロードを使わないといけないのは、numpyなどのように演算の概念を拡張する時なので、その時に参照したらいいのではないか。

14章 イテラブル、イテレータ、ジェネレータ

ここで、繰り返し、述べられるのは、「イテラブル」と「イテレータ」は異なるもの。どう違うかは、お手元のpythonの本で説明してあるのなら幸いですね。何度もこの章で言及されています。

あと、この章は、ジェネレータの説明が白眉です。yield, yeild from, ジェネレータ内包表記を扱う。ジェネレータ内包表記は、ジェネレータ式とも呼ぶと、p23 の 2.2 リスト内包表記とジェネレータ式 で言及してたか。

一行で書くことができるなら、ジェネレータ式(ジェネレータ内包表記)を使い、複数行に渡る処理を書きたいなら、関数定義をして、yeildやyeild fromを使うのが流儀とのこと。pythonの標準ライブラリにおけるジェネレータ関数を説明してくれており、ここをパラパラと見ておくのもいいだろう。

この部分(genexp)を活用するだけでも、かなりpython風に書いているというか、fluentにpythonを書くのに必要だろうという著者の力点を見た気がする。

そして、yeildが、コルーチンに繋がっていることを書いて、16章への「引き」としている。ただ、ジェネレータという概念と、コルーチンという概念を同じyieldというキー・ワードで表しているので、混同しやすい。との注意もある。

コルーチンは、C10Kの解決のため、Nodejsなどでも利用されている概念だし、大昔にMS-DOSで一つのプロセスで、複数の処理をするときにプログラムを書いてちゃんと動かすのに苦労した思い出や、Macが昔協調的マルチタスクという言い方で、イベントループを共有して処理をしていたなとか、走馬灯のように思い出した。

そして、16章、17章、18章は、章を分けているが、順番に必要なことをかき分けている。asyncioを理解して扱うための順番でもある。

15章 コンテキストマネージャとelseブロック

そして、「この章」が なぜ、この位置にある のか、は、まだ良く分からない。この順序である必要性というか。注意深い著者なら何をどの順番で説明するかは、 注意深く決めるものだからであるって、答えはp471に少し書いてある。withとelseはまったく関係ないが、ここに入れると。でも、他の意味ではここに入れているのは 必然っぽい。

with句は、pythonの「書き方」としては知ってはいる。が、活用しているとは言えないので、この本を買った意味の一つでもある。堅牢な処理を書くために活用したいと思っている。

また、for/while/tryで使う、elseは、この章を読んで、使いどころがわかったが、確かに、elseというキーワードはよくないな。syntax sugarで、ensureとかにしたほうがいいんじゃないかと著者の意見に同意した。使うキーワードを増やさないとという python 開発チームの意図とのトレードオフなんでしょうね。preprocessor脳が、邪悪なことを囁くが、CをPascal風味に書くマクロとか 顰蹙 (ひんしゅく) をかっていたのを思い出した。snippetで展開して、毎回コメントにぶちこんで忘れないようにするぐらいか。

15.1 「ああしてからこうする」— if以外でのelseブロックの用法

これはなんというか。snippets案件だな。

  1. for … else は、break/return/continue(これは自明のように思えるが、2重ループなどを考えるとすぐにわかる)/例外で抜ける するかしないかで、if 文を使わずに処理を書きたい時につかうのか。
  2. while … else もbreak/上と同様 するか、しないか。
  3. try … else は、例外/ってか上と同様 がしたか、しないか。

上記のそれぞれの具体例は、p473にある。上記の処理で、ひと工夫する余地があるのではないか。という時には、確認するのがいい。

そして、pythonらしい、用語で、

EAFP(Easier to ask for forgiveness than permission)
「許可を取るより許しを得る方が簡単」

というのが出てきます。EAFPに対比する概念として、

LBYL(Look before you leap)
「ジャンプする前に見よ」as「転ばぬ先の杖」

が、でてきます。code completeだっけ。に出てくる防御的プログラミングというのがいいのか。Pythonの流儀は EAFP なので、他の言語の流儀を取り入れるとしてもなかなか難しいかもしれんね。

15.2 コンテキストマネージャとwithブロック

ここで、コンテキストマネージャって何よ?という疑問というか。定義というか、説明が出てきます。p474

あるコードブロック(コンテキスト)を持った塊を、終了時に確実にやることを保証する、try/finallyパターンをpythonで簡単に扱えるようにしたものという説明です。ここで、登場するダンダーは、

  1. __enter__
  2. __exit__

です。withが開始すると、__enter__ が呼び出され、withブロックが終了すると、__ exit__ が呼び出される。典型例の、file objectについて、例15-1 p474-475 で説明している。

as で、bind する変数を指定している、理由は、__enter__ の戻り値(目標変数(target variable))を保存しているわけね。で、この「目標変数」という表現は、他に使っているのか。と索引を見てもこのページにしかない。ってことは、コンテキストマネージャでしか使わない用語か。

でまた、間違えやすいのは、この2つのダンダ-は、コンテキストマネージャに属しており、目標変数に属しているダンダ-ではない。そして目標変数は、optionであり、例15-1だとファイル操作に必要だから記述されており、with句を使う側に、必要がなければ、利用しなくていい。これや、asを使ったり使わなかったりしてた理由がわかって、すっきりした。

これで、使うときに何をどうしたらいいのか迷わなくて済みそう。

__enter__の引数と、__exit__の引数については、p477を見よ。 (くろまる) の9番で、例外を判定していた理由がわかります。

例15-4の例は、なかなか興味深い。標準出力をオーバーライドさせるテクニックは、頻繁に使うものではないが、知っておくとできることが広がる。p477

p478に、コンテキストマネージャの使用例がある。標準ライブラリに入っているので、 流暢に (Fluent) pythonを書く上で役立つし、自分でどういう時に、コンテキストマネージャを作るのかという例にもなるだろう。

15.3 contextlibユーティリティ

ここの説明が、コンテキストマネージャを作る時に必要なのと、「16章 コルーチン」で生きているのか。

15.4 @contextmanager

ここも、pythonのプログラムを眺めていた時に流していた部分だ。p479 に@contextmanager デコレータの意味というか、扱い方を書いている。これ知らんかったら意味分からんやつやな。なるほどな。書くコードの量は減るが、そういうお約束を知らないとね。

ここでも、yieldが重要な役目う果たしている。

例15-5 (p480)は良い例で、@cotextlib.contextmanager の動作が良く分かる。veersion 3.7で追加された、asynccontextmanagerも気になるが、いまはfluent pythonの内容を優先して発展的な使い方でやるか。

p480-p481にかけて、contextlib.contextmanagerデコレータの動作説明がある。例15-3であった、例外処理が、例15-5ではないので、例15-7で補っている。ここは著者のひと丁寧に書いてるな。一回目的一直線のコードを見せて、こう解決すると追加して理解を深める。

p482にかけて、上記の補っている部分を説明している。デコレータを使うときと、使わないときで、意味合いが異なる。ってのも、知らないと「どハマり」するだろうな。

例15-8(p482-483)も興味深い。読み書きする処理が、こんなに簡潔にかけるのか。@contextmanagerを使っているプログラムの読み方の指南はこれで2回目であり、著者が重要視しているのがわかる。

15.5 本章のまとめ

自分の理解を確認するのに、読み直すのは良い。(p483) やっぱり堅牢な処理を書くには、使いたいよな。

16章 コルーチン

p387

別途、紙に落書きして、ページにはさむことにした。手書きの図を書きつつ整理する。

16.1 ジェネレータの発展形としてのコルーチン

16.2 コルーチンとしてのジェネレータの基本動作

読書会で議論した。

例16-1はすぐに理解できるとおもう。(って、ちゃんと理解できているかどうかは、例16-2で確認されるわけだが。)

p490-p491 についての解説。ここに時間を取られてしまった。

  • yieldの右辺は、イテレータに戻す値なんだ。左辺は、ブロックしていた状態から解除するときにsendされた値なんだ。多分これでまちがえない。
    • yield (イテレータに戻す値) このblockされる前に確定していた値(GEN_SUSPENMDED)した時に評価していた値をイテレータに戻す。
    • a = yield (aは、コルーチンオブジェクト.send(n)された値になる。そして block されていた (GEN_SUSPENDED) 状態から抜けてきた時に代入されている。

自分の文章にしてみたら、たったこれだけのことを導出するのに時間がかかってしまった。fluent pythonの中にも慣れはいると慰めは書いてあるが、使いこなすには理解せずにプログラミングはできないのだった。

ここを理解した後なら、p492の図16-1はわかりやすい。

16.3 その時点の平均を計算するコルーチン

yieldキーワードに到達して、block(GEN_SUSPEND)状態になったら、yieldの右辺の式を、呼び出し元のジェネレータに返す。のと、SENDされたら、yield式から抜けて左辺に値を代入する。と理解するのが良さそうなのだが。

16.4 コルーチンの予備処理をするデコレータ

コルーチンを使うにあたって、デコレータを用いて、確実に、GEN_SUSPEND にする方法を扱う。p494-p495 pythonの標準ライブラリに、これを定義するデコレータはないのかとの質問もあったが。どうなのかは調べていない。でも必要なら「Fluent Python」を読んだのなら、これらのページを含めて書くことはできるだろうということなのかも知れない。

yield from構文と、asyncio.coroutineデコレータへの言及があるので、意味があって「このまま」にしている風である。私が理解できていないだけのようなので先に進んで振り返るのが良かろう。

16.5 コルーチンの終了と例外処理

ここでは、コルーチンにコルーチンが想定していない値をsendしたらどうなるかという話です。p496

  1. コルーチン・オブジェクトに穏便にcloseをする。GEN_CLOSEDになりコルーチンは終了する。(ループ終了?)
  2. 例16-10の説明、p497の例16-8のコードを対象にしている。DemoExceptionという例外が投げられる例で、プログラムは終了しない。loopがおわっていない。GEN_SUSPENDの状態のままである。
  3. 例16-11の説明、p497の例16-8のコードを対象にしている。ZeroDivisionErrorというプログラムで扱っていない例外を相手にすると、コルーチンは終了して、GEN_CLOSEDになる。

コルーチンの終了時に確実に処理をさせるなら、p499の例16-12にあるような、try-finary例題を使え。

ここの記述で、yield from 導入に関する、 (ほの) めかしがある。コルーチンが値を返す

16.6 コルーチンから返される値

p499-p500 例16-13の例は、namedtupleの良い例だと感じる。

これまでのコード例とは、異なり

  1. 無限ループでない。2回めの.send(None)を受けると、ループを終了する。最初のnext(coro_avg)は、yeild文に到達するのに必要なので、というのはこれまでの議論を読んでいた人には、冗長かもしれないが、動作としてのコメントは残しておく。
  2. このコードは、breakしたあとに、returnで、namedtupleを一つ返している。昔のpythonでは、エラーを返していたが、 2020-08-18 現在のpython 3.8では起きないので、興味があれば本書などで確認するのがいい。

例16-14 の解説

  1. ●1の解説は、当然でyeild文の右辺に値がないのだから、値をyieldしないのも当然かと。
  2. ●2の解説は、ジェネレータをforで使っているときと同じ、とあるがFluent pythonのどこで書いていたは、また後で指し示すことにする。どこかで読んだが、14章だったか。

16.7 yield from

ここでの、yield fromの用法は、14.10 yield from p456 と相似している。このページで言及しているのは、p501からになる。しかし、飛ばし読みをするのは、おすすめできない。ここまでの説明なしにいきなりp501にきても前提としている知識が足りないと思うから。

fluent python で言及している PEP 380は下記である。

p502から、p503に書いてあることを読むと、PEP 380に書いてある、双方向チャンネルの下りがなにをいうているのか分からないが、図16-2を見る。

公式ドキュメントのPEP 380のサンプルコードに、printデバッグを挿入して、動かしてみたら、図16-2の言わんとする所がわかったと思う。それを掴んでしまえば、p504からp505にあるスクリプトの内容と、p506から始まる説明の内容が理解できました。

p506のカラスの部分ある「自分で理由を考えてみよ」というのも、Noneが送られるまでは、yield fromから出てこないので、処理が終わっていないという、yieldの動作を確認する話だと理解しました。

サブ・ジェネレータは、終了条件を満たすようになっているが、●12の処理がないと、「未完の処理(yield fromから抜けてこない)のが出てくる。つまり処理した結果が確定しない。この未完の処理がいるかどうかを確認する方法は、●9で得られたジェネレータオブジェクトを、inspect.getcoratestate()すると分かるのだろうが、このプログラムだと保持していないからわかんないか。

なんか、べつの方法でモニタリングできないのかな。プログラミングを間違えなければ良い話ではあるし、プログラム時に保持しておけば良いかもしれないが、処理が終わったらガベージコレクタにメモリを開放してもらいたいから、何でもかんでも保持するのは良くないか。

16.8 yield from の意味

この節の内容は、じつはPEP 380の要約というか説明することで、yieldへの理解を深めるという意味があるのかと思って読み始めたら、サブ・ジェネレータがエラー処理を上位のyield fromにあげてくる、「real world」な状況について書いています。例外のトラップの話がでてきます。結局asyncioの理解に、コルーチンというか、yoeld from は頻繁に出てくるそうなので、またここに戻ってくることもあるだろう。

ここの概念は、16.11 参考文献のp526 で、追補されている。ちょっとだけ力づけられる言葉としては、p526の下から6行めからの引用である。

yield from のコードが難しいかどうかは、「協調的マルチタスク」の概念を理解しているか。によるとある。

16.9 コルーチンを使った離散事象シミュレーションの事例

具体例については、一度通読したあとに戻ってきます。

16.9.1 離散事象シミュレーション

タクシー運行管理シミュレーション

16.10 本章のまとめ

Guido van Rossumの言葉を引用して、ジェネレータには3つのパターンかある。とのこと。ある意味デザインパターンに近いのかも。

  1. 従来型の「プル」(イテレータ)
  2. 「プッシュ」(Fluent Pythonで何度か出てきた平均の計算)
  3. 「タスク」:A Curious Course on Coroutines and Concurrencyの資料を読んだかを問われているっぽい。ってか、このタスクは、18章でやることになるようだ。
  • デコレータで、next()するのは良し悪しで、yield from subgenerator() は、subgenerator()がnext()されていない想定で動いているので、混ぜると思っているように動作しない。というのは、覚えておく必要がある。

協調型のマルチタスクの説明が、あっさりとp524-p525に書いてあり、協調的とは異なるタイプである、プリエンプティブ・マルチタスク preemptive multtask については、区別を説明できない人はまとめておくのがいいだろう。あまりにもあっさりと書いてあるので、経験者というか、この二つを区別できる人でないとこの部分は意味不明だろう。

@asyncio.coroutineデコレータの内容が話されているが、async defとの話はどうなるのか。

  • python - @asyncio.coroutine vs async def - Stack Overflow
    • disを使って、同じ内容の中間コードが生成されているのは良い。
    • ただ、 2020-08-18 の段階では、python 3.8が使われているので、3.5以前の、@coroutineの扱いについては、用意できる環境に左右される。スクリプトの実行環境チェックは、入れるのが正解なんだろうな。

16.11 参考文献

p526の下から3行めから、p527にかけて、async defの話をしている。async defで定義された、処理の中では、yield/yield fromは使えないということなので、概念というか機能としては重要だが、pythonのコルーチンの扱いは、この本がかかれた後も変化しているだろうから、キャッチアップが必要そうです。

ってか。p350で、「最新ニュース」として、PEP 492 の話題がでてる。ってことで、asyncioについての記述は、翻訳者さんと監修者さんがどれだけフォローしているのか確認しつつpython 3.5関係のupdateを見ないとあかんのか…

17章 futuresを使った並行処理

2020/08/19 にやる Fluent Python読書会ための基礎資料

この冒頭の引用は、なんの話をしているのか。

シンプルなパターンだけで済むのか。マルチスレッド・デザインパターンは考慮に入っているのかとか、疑問があるが、まずは読み進めて著者の主張を聞こう。

  • concurrent.futures ライブラリ
用語 意味 ソース
concurrent/並行 処理のはじめから終わりまでの時間が複数のタスクで重なっているときに使われます。たとえば、シングルコア上で動作しているマルチタスクOSのように、複数のプロセス自体は同時に動作していていても、ある一瞬では1つしか動作していません。 Fluent Python p532の脚注†2
並行処理(concurrency)とは、 たくさんのことを一度に「対処」することです。 Fluent Python P545
parallel/並列 たとえば複数のCPUで複数のタスクが同時点に動作しているときに使われます。 Fluent Python p532の脚注†2
並列処理(parallelism)とは、 たくさんのことを一度に「処理」することです。 Fluent Python P545

Fluent Pythonでは、18章 P565の定義を使うようだ。並行と並列って、人によって使い方が違うので、適当に読んでいると、さっぱり分からなくなってしまいがち。

ちなみに、「実践Python3」には「4章 高レベルな並行処理」がある。

17.1 3種類のウェブダウンロードスクリプト

https://www.youtube.com/watch?v=A9e9Cy1UkME

これを見ておくと、本書の議論を具体的にわかる。一番いいのは実際に動かすことだが、DDoS攻撃をしたくないという人には有益だろう。

17.1.1 逐次型ダウンロードスクリプト

pythonの標準 urllib系は、人によって何を使うのかの意見が異なっている。Fluent Pythonにおいては、requestsを推しているようだ。ちなみにpython3でもurllibの使用例はある。python2での互換などを考える人は調べるか、書き直すかなどの選択があるだろう。

例17-2

17.1.2 concurrent.futures を使ったダウンロードスクリプト

この例はスレッドプールを用いている

17.1.3 futuresはどこ

futures の意味する所を書いている。これ重要 p537

17.2 ブロッキングI/OとGIL

内部的にスレッドセーフでない、CPythonインタプリタには、GIL(Global Interpreter Lock)が用意されている。

上記の引用は、Pythonの批判に上がるが、それについての解説。

まずGILとはの説明

処理系名 GILの有無
CPython あり
PyPy あり
Jython なし
IronPython なし

GILがあったとしても、Pythonの実装から下のLayerにいけば、1つのPythonを実行しているプロセスが複数のCoreを活用できないとしても、C言語などが扱うライブラリの世界においては、GILはないのでCPU Coreを活用できる。

I/O待ちで、OSに処理が渡っている時間は、Pythonの管理下ではなく、「そのOS」の管理下にあるから。ここ重要

処理をぶん投げて、結果を待つスタイルのプログラムを上手く書くというのが、 流暢なPython (Fluent Ptyhon) なコードの書き方だ。Python風だ。ということだと思う。

17.3 concurrent.futuresによるプロセスの機動

coreが4つの場合、workerを1から4に変化させて、処理速度がどのように変わるか。P543

CPUを使わずに、IO待ちになる処理

17.4 Executor.mapの実験

例17-6 の例いいな。このログ表示は「このプログラム」の条件でわかりやすい。P544-547

17.5 進行状況表示とエラー処理を加えたダウンロード

実世界のプログラミングでは、人間へのフィードバックという大事な「進行状況表示」と不測の事態は起こるので、「エラー処理」は必須です。

TQDMとかのライブラリが充実しているのも、Pythonのいい所だ。

17.5.1 flag2のエラー処理

17.5.2 futures.as_completedの利用

17.5.3 threadingあるいはmultiprocessingによる方法

ここに持ってきているのは、これまで議論してきた内容を先につたえたいのと、GILが問題になるという話、歴史的経緯から、threadからthreadingへの移行があったからか。

Fluent Pythonとしては、multiprocess の話は概略にしているが、最初はガリガリと書いていたが「この分量」であるし、並列処理については、それだけでやるべき論点が多すぎるのだろう。

基礎は教えたから、自分で調べろということのようだ。

17.6 本章のまとめ

17.7 参考文献

Soapbox

ここも、読んでいて面白い。他の言語を比較するなら、読んでおけということか。

プログラムか単純にかくために、抽象化されたworkerたちを動かしたい。ガチなスレッドプログラミングはできる人に任せたいというのが、動機というのは、理解できる。全部を一つの言語でカバーはできないのだから。

18章 asyncioによる並行処理

18.1 スレッドとコルーチン

Never use time.sleep() in asyncio coroutine. everything will be stop.

Use yield from asyncio.sleep(DELAY)

18.1.1 ノンブロッキング型の asyncio.Future

18.1.2 Future、Task、コルーチンからのyield

asyncioおよびaiohttpによるダウンロード

18.3 ブロッキング型の呼び出しをぐるぐるまわす

18.4 asyncio ダウンローダスクリプトの拡張

asyncio.as_completedの使い方

Exeecutor を使ったイベントループのブロッキングの回避方法

ここのテクニックは、他でも有効な場面はいつか。

18.5 コールバックからFutureとコルーチンへ

コールバック地獄と解決方法

18.5.1 1回のダウンロードにつき複数回のリクエスト

18.6 asyncioサーバを書く

18.6.1 asycio TCPサーバ

18.6.2 aiohttpウェブサーバ

18.6.3 並行性を高める賢いクライアント

18.7 本章のまとめ

18.8 参考文献

Soapbox

とは、いいながら、色々とasyncなライブラリが作られていて、どれを使うのかを試されている。

19章 動的属性とプロパティ

この19章と20章を理解すると、pythonで、RoRのactive record/DjangoのORM/SQL AlchedmyのORMのような、プログラムを書くことができる模様、この辺の知識は、対象に応じて動的に生やすことを求められる時に有用で、抽象度が上がるから、通常のアプリケーションプログラマだと使わないのかも。

動的にプログラムを組み上げるとき(Meta programming)と組み合わせると、動的に対応できるので、固定的にコードを書くよりも動的にコードを生み出すときに真価がでるのだろう。

  • 「属性(attribute)」(用語集 p781)
    • obj.attrName のような形(objは、生成したinstance/objectにattrNameという任意の属性名使って、値の読み出し、書き込みを行う。そして、この19章においては、データを読み取る先が、単純な場合とは限らず、データの取得先が、RDBであったり、通信先であったりなどのときにどうpythonであつかうのかについて、書いている。
  • 「統一アクセス原理」(用語集 p783)

19.1 動的属性を用いたデータの変換

  • JSONデータの提示
  • pythonによる、JSONデータの読み取りプログラムの提示
    • どういう風に、JSONデータが表現されているかを確認

19.1.1 動的属性を用いたJSON風データの取得

  • JavaScriptのように、ちょっとだけ簡易な記法を使うには。p618

19.1.2 無効な属性名という問題

  • 属性名が、pythonのキーワードと被ったら、エラーになるんだけど、どうしたらいい?
    • これが、pythonの開発者たちが予約語を増やしたくない理由の一つ?
    • pythonの予約語と衝突していたら、衝突しないように変換したらいい。
    • その方法は? p622

19.1.3 __new__を用いた柔軟なオブジェクトの作成

  • __init__

    • 習慣的に、コンストラクタ・メソッドともいうが、実際にpythonでのコンストラクト・メソッドは __new__ である。p623
    • イニシャライザーと呼ぶのふさわしい。
  • __new__

    • classmethod
    • でも、@classmethodデコレータは使われない
    • インスタンスを返す
    • objectからの継承のコードで充分なことがほとんどで、こまらない。
    • __new__ から、__init__を呼び出している。p623 擬似コードあり。
    • __new__ で、新しいオブジェクトを作る例 p624
  • OSCONのJSONデータの問題点

    • 氏名はそのまま表示できず、別のリストを走査しないと得られない。

19.1.4 shelveを用いたOSCONデータの再構成

  • pickleは知っている前提
    • シリアライズの概念

p625 で軽く説明しているが、足りないなら下記を参照せよ。

RDBなら、viewを作って解決してしまう問題ではあるが、ここは動的属性を扱う例題であるので、著者の仮定にそのままのっていく。

Recordデータの生成方法と、インスタンスの作り方を学ぶ。そして、Recordデータを自動的に検索するプログラムについて学ぶ。 p627

  • 「大量の」属性をインスタンスに、作成するpythonicな手法 p627の下から4行目から。

p628

19.1.5 プロパティを用いたリンクレコードの取得

ここは、eventレコードから、venueやspeakerという属性を引いたときに、シリアル番号でなく、人名などが入ったデータという、「完全なレコードオブジェクト」として返す部分の処理の はなし。

ここで読み落としそうになったのは、p633の問題点

19.2 プロパティを用いた属性の検証

19.2.1 注目品目のクラス(LineItem テイク1)

19.2.2 注目品目のクラス(LineItem テイク2)

19.3 プロパティとは何か

ここで、組み込みクラスであることを示している。p638 デコレータにしてきた理由でなく仕組みを説明している。やはり、ある程度pythonプログラミングについてわかっている人向けの説明なのである。

19.4 プロパティファクトリ

19.5 属性の削除処理

19.6 属性処理の重要な属性と関数

19.6.1 属性処理で用いる特殊な属性

19.6.2 属性処理で用いる組み込み関数

19.6.3 属性処理で用いる特殊メソッド

19.7 本章のまとめ

19.8 参考文献

Soapbox

20章 属性ディスクリプタ

属性
pythonポケットリファレンス p163

オブジェクトの持つ属性を調べるのに、 dir() の結果で確認します。

20.1 属性の検証処理

属性には、2つの種類がある

  1. インスタンス属性
  2. クラス属性

表面的には、obj.xのような形によって、値を設定するものという理解でいいが、クラスに設定する「値」は、クラス属性であるが、メソッドに関しても実はクラス属性である。

ディスクリプタにも2つの種類がある。

  1. データディスクリプタ
  2. 非データディスクリプタ
  • Python を支える技術 ディスクリプタ編 #pyconjp - Qiita
    • この記事は参考になった。PyCon JP 2014 での発表内容とのこと。
    • この記事のおかげで、ずいぶんと不明点が埋まった。
    • ここの定義は、下記です。
      • データディスクリプタは、__get__ と __set__の両方を定義しているディスクリプタ
      • 非データディスクリプタは、__get__だけを定義しているディスクリプタ

「19.4 プロパティファクトリ」(p642)で既にやった(例19-24, p643-644)を、ディスクリプタ・クラスにリファクタリングするのが、次項になる。別解ともいえる。

20.1.1 単純なディスクリプタ(LineItem テイク3)

p658 の本項にて、Fluent Pythonにおけるディスクリプタの定義がなされている。

ディスクリプタ
__get__, __set__, __delete__メソッドを実装するクラスがディスクリプタです。

p658-p660にかけての文章というか、定義はどのタイミングで必要になるのか。

  • p661の●3が、__set__の引数、(self, instancee, value)は、
    • selfはディスクリプタインスタンス(定義は、p659)
      • LineItem.weight
      • LineItem.price
    • instance は、「管理対象インスタンス」(定義は、p659)
      • LineItemインスタンス
    • valueは、代入される値

p661の●4で、別件で気になっていた記法が、無限再帰をしないためのテクニックであることがわかった。

p662 のサソリのマークにある注釈で、p659の定義をしている理由になる。というのが、著者の気遣いなのだろう。

そして、 重複 (ちょうふく) があるのが、良くないよね。という引きをして、次の項に移る。

20.1.2 ストレージ属性名を自動的に生成(LineItem テイク4)

管理対象属性とストレージ属性の名前が異なるので、

ここやな、名前が異なる これが違う。

p665のカラスの注釈は、クラスを生成する毎に、カウンターをインクリメントするという単純な仕組みだが、重複しない名前を生成することができる。

p667のカラスの注釈は、デバッグ時に助けになる情報は、21章 クラスメタプログラミングでやる、クラスデコレータまたは、メタクラスがいるという引きになっている。

p668から、p669にかけて、プロパティファクトリとディスクリプタクラスについての実装し直しがある。比較検討するなら、ここを読むといいようだが、pros/consとかも著者に賛成できるかどうか。もあろうかと思います。あとは、両方の書き方が存在する以上、自分で新規に書く時に選べば良いし、両方理解できないとコードの理解ができないので、これはコレで良い部分です。

20.1.3 ディスクリプタ型の追加(LineItem テイク5)

前提条件のストーリを確認せよ。

class C:
    class D:
        def meth(self):
            pass

C.__qualname__

C.D.__qualname__

C.D.meth.__qualname__

20.2 オーバーライドディスクリプタと非オーバーライドディスクリプタ

  • オーバーライドディスクリプタ
    • __set__メソッドを実装したディスクリプタ (p674)
  • 非オーバーライドディスクリプタ
    • __set__ メソッドを実装しないディスクリプタ(p676)

とは、どんな概念なのか。

20.2.1 オーバーライドディスクリプタ

属性の扱い方が、Read時とWrite時で異なる。

  • Read時

    1. インスタンスに「対象の属性」がある。そのままインスタンス属性を取得する。
    2. インスタンスに「対象の属性」がない。クラス属性を取得する。
  • Write時(代入時)

    1. インスタンスに代入時する。そのままインスタンス属性に代入する。クラスには何もしない。
  • vars: 組み込み関数 — Python 3.8.5 ドキュメント

    • __dict__ 属性を持つオブジェクトの、 __dict__を返します。

20.2.2 __get__のないオーバーライドディスクリプタ

20.2.3 非オーバーライドディスクリプタ

p677

サソリの部分で、ようやく、Fluent Pythonが使っている言葉と、他の人が使っている人の言葉について対応を述べている。

  • オーバライドディスクリプタ は、
    • データディスクリプタ とも呼ばれる。
    • 強制ディスクリプタ とも呼ばれる。
  • 非オーバライドディスクリプタ は、
    • 非データディスクリプタとも呼ばれる。
    • シャドーイング可能ディスクリプタ とも呼ばれる。

ディスクリプタと同名のインスタンス属性に代入すると、__set__メソッドのありなしで、結果が異なる。

20.2.4 クラスディスクリプタの上書き

__set__ のディスクリプタが思い通りに動くか?

21章への引きとして、メタクラスで問題解決できる余地を提示している。

20.3 メソッドはディスクリプタ

結合メソッドについては、p678の†8にあるように、8.5節をざっと見たが、「結合メソッド」という言葉はないのだけども。

ってか、何をやっているのかわからん。が、まずは先に進んで戻ってくるイメージで。

20.4 ディスクリプタの使い方

  • ここの、実用上の効果を読んで、学習する意欲を高めよう。p681 ぎゃくに有り難味を感じないのなら、この本はまだあなたに向いていないかも。

20.5 ディスクリプタのdocstringと削除処理のオーバーライド

20.6 本章のまとめ

20.7 参考文献

Soapbox

selfの問題

ここで、pythonに対するselfの扱いについて、弁護している。

21章 クラスメタプログラミング

20章の途中から、難易度下げてる? ちゃんと書いてるやん。

クラスメタプログラミングについての妥当そうな定義を書いてる。p687

メタクラスは、強力過ぎて、もっと簡単な方法で解決するクラスデコレータを使うことで解決ことが多い。ここの章でも、

  1. インポートタイム
  2. ランタイム

の区別が必要とのこと、Pythonにおける(クラス)メタプログラミングをするのに必要な概念だそうです。デコレータの時にも書いた気がする。

知ってて使うなという概念を学ぶのは楽しいな。

21.1 クラスファクトリ

snippetを使って書くべき所を動的にコードを書く。

21.2 ディスクリプタをカスタマイズするクラスデコレータ

21.3 インポートタイムとランタイムに起きること

21.3.1 評価のタイミング

メタクラスの基礎

21.4.1 メタクラスの評価タイミング

21.5 ディスクリプタをカスタマイズするメタクラス

21.6 メタクラスの特殊メソッド __prepare__

21.7 オブジェクトとしてのクラス

21.8 本章のまとめ

21.9 参考文献

Soapbox

あとがき

仮置場

プロトコル

fluent pythonを読んでいて、用語集に「プロトコル」がないのが気になっている。ガンガン用語として使っているのに、はっきりと説明せずに外堀を埋める形で、定義しようとしている感じです。

fluent python P317の「非形式的なインターフェースとしてのプロトコル」という文章があった。ようやく著者の意図が説明されてすっきり、やっぱり用語集に入れておく価値があると思うな。本に書き込むか、私的用語集にぶっこんでおくのがいいんだろうな。

pythonと、「トレイト」について調べていると、たまたま見つかった文書 [翻訳] Python プログラマーのための Rust 入門 - Qiita があって、「トレイト対プロトコル」という見出しがある。

Python では、特殊メソッドを実装することでクラスは特定の振る舞いを選択できます。これは通常 “プロトコルに従う” と呼ばれます。

この表現は、現段階でfluent pythonを読み進めていてしっくりとくる表現です。特殊メソッドは、ダンダともいうので、同じことを別の名前で表していることがあるので、「本を読む本」のいう「シントピカル読書」が有効だというのも納得です。多分簡単過ぎる表現にすると、間違った理解に至るのを嫌っているから、こういうやり方なのかも知れない。

こうやって自分で、調べていくと忘れないからいいけど。

トレイトに関しては、「コーディングを支える技術」を参照してほしい。ScalaやSqueakの例があるとのこと。上記linkで Rust も。

ジェネリック

言葉としてはよく聞くが、具体的な定義なしにフワッと使っていないか?読み落としているだけかもしれないが。動的言語なので、どのクラスに入っても有効に使える関数として書くという意味であれば、下記のmixinに近接した概念になるのではないか。下のmix-inも参照せよ。

mix-in

  • [Python入門]多重継承とmixin (1/2):Python入門 - @IT

    • 多重継承すると、何が問題となるか。、別のクラスの変数は、同じ名前で、同じクラスに入れても「別の変数」である。
    • あ、ここの説明は、継承関係は派生した側(下側)から、親に向かって矢印を引く流儀や。
    • mixinっていうても、継承の使い方で、「特別なキーワード」をつけて別の呼び方をしている訳じゃない。
    • mixin側は、self経由でごにょごにょするコードを書く。ここの意味が、Effective pythonのp74にある「Pythonでは、mix-inを書くのが容易です。それは、型にかかわらず現在の状態を調べるのが簡単にできるからです。動的インスペクションのおかげで、他の多くのクラスにも適用できるジェネリックな機能をmix-inで一度に書くことができるのです。」ということで、Pythonでいうジェネリックという概念は、この意味であると仮に理解しておく。後でまた変えるかも。
    • クラス階層という言葉があり、mix-inは、どのクラス階層にも入れることができるとあるが、これって、「どこのクラスにも入れることができるように書いている」から、できるのであってなんかトートロジーな説明ではないか。とも思える。
  • [Python入門]多重継承とmixin (2/2):Python入門 - @IT

    • なるほど、mix-inしたクラスで、テンプレートメソッドという言葉を出している。が、これって abstraction している部分と、implementation しているのを分けているともいえるので、同じことを別の名前で呼んでいる可能性もあるのでは。さしずめオーバーライドが、インプリメンテーションってか実装、か。ルー大柴っぽくカタカナ語が多いが、多分そういうことか。
    • 自分で実装しろよ。というやり方もこれね。
    • 最後まで読むのは会員登録がいるが、ここまで書いてあれば、なんとかなるか。
    • ってか、Effective Pythonの「項目24:@classmethnodポリモルフィズムを使ってオブジェクトをジェネリックに構築する」p64-69 に収斂していくのでは、クラスで実装するパターンとして。
「コーディングを支える技術」
「12.3 多重継承の問題点」- 解決策3:処理を混ぜ込む(Mix-in) pythonの場合 p230 言語的なサポートを受ける、受けない。慣習としての話がある。Pythonは多重継承できるので、「コーディングを支える技術」の 12.2 多重継承 「実装の再利用に便利な多重継承」p219を挙げておく。「コーディングを支える技術」12.2多重継承の問題点—またしても衝突! 「提供するメソッドと要求するメソッド」p233において、書いてある問題点として、 Mix-inをする場合に、単体ではインスタンス化してはいけないクラスがあるが、どういう状態になればインスタンスを作っていいか判別する仕組み言語上のサポートがない というものでした。

上記って、Pythonの「抽象基底クラス」が、解決策のように現段階ではみえる。Fluent Python P782の用語集「抽象基底クラス」ただ詳細をこれから読むのだけども。p336を読むと、インスタンス化する時に、TypeError例外を送出するように、プログラムを作っておくことができるので、「コーディングを支える技術」の著者が主張する、インスタンスを作っていいかを判別する仕組みにとは何かを読み解かないといけないか。

effctive python

もう第二版が出ているので、初版の正誤表はなくなったのかもしれんが、索引の mix-in クラス の 定義が、73-74は間違っていないか 多分、74-75ではないのか。


  1. ダンダーの話は、p4に載っています。 ↩︎

  2. p85の下から1行目 ↩︎

  3. 誤解させる言い方ですね。正確には、低レベルのレイヤを対象する技術者、アセンブラ/C言語でのシステムプログラミング以下、組み込み系とか、OSとかを触る感じの人です。 ↩︎

  4. REPLのことをコンソールセッションと呼び、関数を「ランタイム」で生成しているので、実行可能バイナリのことを指してランタイムと呼んでいるわけではなさそうです。 ↩︎

  5. LISPの話? clojure - common lisp wikipedia - 解決方法 や一般的には、多重ディスパッチ - Wikipedia とも呼ぶようだ。 ↩︎

  6. 識別子というか、このIDって実装依存なんや。 ↩︎