More Joel on Software

www.shoeisha.co.jp

過去にアリエル・ネットワーク (以下アリエル) という会社で働いていました。これはもう10年前の話で、アリエルはオンプレミスで運用するパッケージベンダーの会社だったので、昨今の SaaS のようなプロダクト開発とは状況が大きく異なります。そういった時代背景の違いを考慮して本稿を読むように注意してください。

当時の課題管理や開発方法論が、その前もその後も、10社以上、十数の開発チームで働いた私の経験の中ではもっとも開発の生産性も開発体験 (Developer Experience) も優れたものでした。

たまたま、というよりは、私がお願いして、当時の上司と雑談してみました。当時のアリエルの課題管理システムには、自社パッケージの開発に関する内容 (プロダクトの機能開発や不具合など) だけでなく、顧客からの問い合わせや開発者のTODOやシステム管理のメモなど、会社の多くの情報が入っていました。プロダクト開発に関するすべての情報はたった1つのプラットフォームに集約されていたので情報の一元管理という視点からみると非常に強力なプラクティスでした。

多くの情報が課題管理システムに入っているので、営業もコンサルタント (アリエルでは顧客と開発者の仲介役) も、マネージャーも開発者も、そのチケット上でコミュニケーションをしていました。最近だと、チャットツールで他チームや他部署の人たちとコミュニケーションを取ることが多いと思いますが、アリエルでは課題管理システム上のチケットで行われていました。当時も Skype のグループチャットはあったものの、課題管理システムがコミュニケーションのメインで Skype はアナウンスなど補足的な用途でした。

そんな話題を上司としていたとき、あのやり方は Joel on Software に由来するのだと教えてもらいました。具体的には次の記事です (この記事は本書には含まれてなかった) 。

www.joelonsoftware.com

そんなきっかけで、私が Joel on Software に興味をもち、本書を読んでみた次第です。本書は2000年代に書かれた内容を出版したものなので、いまとなっては古典のような内容も多いです。とくに特定の技術について言及している内容は、当時の状況ではそうみられていたんだなとか、歴史的な読みものとして理解することも多いでしょう。本稿ではなるべくいまでも通用しそうな内容に絞って紹介することにします。私はいまマネジメントに関心があるのでその内容が多くなります。

第2章 優れた開発者を見つけるには

基本的に転職市場には優れた開発者がいないという前提を述べた後、次の方法を提案しています。

  1. こちらから出向く

    • ホットな新技術のカンファレンスに出かけて廊下をぶらぶらして会う人に声をかける
    • 優秀そうな人をみかけたらスカウトする
  2. インターンシップ

    • 大学生の優秀なプログラマーをスカウトする
    • 大学生はあまり採用や就職活動に知識がないのでスカウトしやすい
    • インターンシップのパイプラインは長く、途中で多くの人が失われる

  3. 自分のコミュニティを作る

    • 似た考えをもつ優秀な開発者のコミュニティを会社の周りに作り上げる
    • できればうまくいくが、コミュニティを作るのがとても難しい

これらはいまでも採用活動の一環として行われているようにみえます。最後のコミュニティを作るというのは、多くの会社がテックブログを書いていて、読者となる開発者の興味・関心を集めようとしているのがもう少し簡単な施策かなと思います。

またリファラル採用について、著者はそれほどこの採用方式を信用していないと述べています。

  • 優秀な社員が優秀な開発者の友だちをもっているのは事実だが、優秀じゃない友だちもたくさんもっている
    • 不採用になると友人関係にヒビが入るから本当の友だちを紹介しようとしない
  • 最初の採用プロセスをパスする程度で新規採用のソースとしては最も弱いもの
  • あくまで通常の採用プロセスをすべて通過するかどうかを判断基準にしている

リファラル採用よりも自社の採用プロセスを重視していることが伺えます。リファラルによって採用のバーが変わらないという点が大事なのかなと私は理解しました。

第3章 開発者観察ガイド

同僚はどんな人たちか?

著者がマイクロソフト社からもってきた採用ルールに、自分たちが付け加えたルールが次になります。

嫌なやつでないこと

アリエルの CTO もたしか飲み会のときに「アリエルの開発には嫌な人がいない」が仰っていた気がします。私も辞めるときにそのことを意図的に書いているので間違いないです。

開発者の視点からみたとき、一緒に働いている同僚に嫌な人がいなくて、同僚の技術スキルが高いというのが魅力的です。

アリエル・ネットワークを退職しました - forest book

昨今ではコンプライアンス対策からハラスメントを禁止する風潮もあり、若い人にとっては嫌な人がいないのが当たり前の職場しか経験していないかもしれません。それはよいことだと思います。いまは、技術スキルが高くて優秀な嫌な人になるのが難しい属性になっているように私は思います。それは技術の発展に伴う多様化・複雑化が要因の1つではないかと考えます。

2000年代はまだ1人の開発者がその事業部やチームがやっている技術の大半を把握しているといったことが、いまよりは可能でした。嫌な人だけど、技術スキルが高いから誰も文句言えないといった人が職場にちらほらいた気がします。

いまは技術の要素が多岐に渡り、それぞれが専門化・分業化したことでその事業部やチームがやっている業務すべてに熟達するのが難しくなっています。特定分野の技術スキルが高くても、他チームと協調したりお願いしたりすることが昔より増えました。結果として、嫌な人は活躍できなくなってしまい、そういう態度を取るデメリットの方が大きく、採用時点でも嫌な人特性を排除することを重視するようになり、いまでは淘汰されてしまったのではないかと私は思います。

いまは逆に、心理的安全性の文脈で、同僚に適切な厳しい指摘をするのが難しくなってしまった雰囲気はありますが。

誤解を招く表現だったので補足します。本来の心理的安全性の文脈では、同僚同士で適切な厳しい指摘をし合える関係のことを指します。しかし、それはお互いの信頼関係が前提になっています。そうじゃない状況において相手を尊重し、信頼関係を築いていくことから始めるでしょう。問題なのはそれがずっと続いてしまい、そのまま、ぬるま湯のチームが出来上がってしまうことです。メンバーが誰も厳しいことを言わないチームは永遠に問題ばかりをエスカレーションして、ビジネスの問題解決力に劣ったチームになってしまいます。

独立性と自律性

この考え方は、会社や組織、チームによっても解釈が変わってきそうな気がします。著者は優秀な開発者を次のように扱うべきだと述べています。

基本的に、頭の良い人を雇おうとするなら、彼らがその能力を仕事に適用できるようにしてやる必要がある。マネージャはアドバイスできるし、自由にそうしてかまわないが、その「アドバイス」が命令と受け取られないよう、細心の注意を払う必要がある。

(中略)

開発者は自らの能力によって雇われ、専門家として遇され、その専門領域に関しては決断させてもらえることを望んでいるものだ。

先日、上司と雑談したときも、課題管理システムの他者が担当者のチケットにコメントすると若い人は「命令」と受け取ってしまうのであまりしないようにしているといった話しを聞きました。チームで相談したり議論したりすることは良いことだと思いますが、なにかを決断するとき、その担当開発者が決められるようになっているか。みなさんの組織ではどうでしょうか。

政治がないこと

プログラミングの世界は非常に公正で、非常に厳格に秩序立てられている。そもそもプログラミングの世界に入ってくる人がそうする理由は、多くの場合、公正で、秩序があり、厳格に能力主義で、議論は単純に 正しいほうが勝つ ような場所にいたいと思うからなのだ。

プログラマが「政治」について文句を言うとき、それが正確に意味していることは、技術面よりも個人的な考えが重視されているということだ。

これも営業やビジネス部門がめちゃくちゃな機能や納期を要求してくるといったことは減ってきて、開発者が目の前の課題に集中しやすいようになってきたのではないかと、私の周りではそう思えます。しかし、業界や業種に依るのかもしれません。

プログラマが気にしないもの

彼らはお金を気にかけない。あなたが別なことでひどいことをしない限りは。

誤解のないよう、著者はプログラマの給与を低くしてかまわないということを主張していません。前述した通り、プログラマは公正さを気にかけるので、業務や能力に応じた適正な給与でないと怒りを覚えると補足しています。そのため、本節の内容は適正な給与を支払った上での前提であることに注意してください。

給与よりも業務内容や職場環境の居心地の良さを優先するので、給与の多寡は優先順位が低いことを理解しておく必要性を説いています。もう1つ、興味深かったのは、プログラマがこれまで不満を述べていなかった給与に言及するようになったら、その本音は給与ではなく、やっている仕事に不満をもっているという兆候であると述べています。要はやりたくない仕事をする代わりに見合う対価がほしいといった理屈だそうです。

さらっと書いてあったのですが、ソフトウェア会社の経営者は考慮しておくとよさそうに思えました。

第7章 一体化マネジメント法

著者の知っているマネジメント方法として3つあげています。

  • 指揮統制マネジメント法 (軍隊のようなやり方)
  • 入門経済学マネジメント法 (経済合理性を重視したやり方)
  • 一体化マネジメント法

前の2つもそれぞれ章を割いて説明していますが、本稿では省略します。著者は自分の会社で一体化マネジメント法と呼ぶマネジメントのスタイルを取っているようです。いま聞くと、よく知られたプラクティスが背景に思い浮かぶ人も多いと思います。著者の名誉のために言及しておくと、この記事が書かれたのは2006年8月10日です。

入門経済学マネジメント法でやっちゃいけないマネジメントとして、内発的動機づけを外発的動機づけに置き換えてはいけないと強調されています。例えば、プログラマが自分からやりたいと考え、自律的に行動して改善した行動に報酬を支払うといったインセンティブを与えることです。これは 過剰正当化効果 と呼ばれるそうです。

余談ですが、最近、メタ認知に関する認知心理学の入門書を読みました。この本の中でも動機づけは次のような順番になるのがよいと書かれています。

  • やる気のない段階 → 外発的動機づけ段階 → 内発的動機づけ段階

内発的動機づけによる行動に報酬を与えると、内発的動機づけを外発的動機づけに置き換えてしまい、外発的動機づけは内発的動機づけよりもずっと弱い動機になるので意欲を失わせてしまうことにつながるというわけです。その入門書では副作用のない安全な報酬として、ことばで褒めることを紹介しています。ことばで褒めても動機づけは置き換わらないことが知られています。但し、能力を褒めると失敗できないとプレッシャーになってしまう人もいるので、成果そのものを褒めるようにした方がよいそうです。

note.com

著者の言う、一体化マネジメント法は、この内発的動機づけを最大限発揮できるよう、社員の自律的に行動した結果が組織の目標につながるようにマネジメントの工夫をしているということでしょう。そのための施策として著者は次の2つをあげています。

  1. 昼食を同僚と共にする
  2. みんなに情報を渡す

後者は「情報の透明性」について言っていて、ホラクラシーやティール組織を成功させる要素の1つにもなっています。私の周りでは、比較的、小規模な組織では原則すべての情報を社員に開示することがプラクティスとして浸透している気がします。意図的にしろ、そうでないにしろ、情報の非対称性は組織の理不尽を増幅してしまうと組織論の本で読んだこともあります。これについてはあまり説明の必要はないでしょう。

前者の同僚と昼食を共にするというのどうでしょうか。私も過去に働いてきた会社では、だいたい同僚と一緒に昼食をとっていました。いま思い返すと、昼食を共にしていた上長や同僚と業務で対立したことがないことに気付きました。私は理屈の通らない業務やマネジメントにはクレームする方だったので、何度か上長と業務で険悪になってしまったこともあります。しかし、一緒に昼食をとっていた上長と険悪になったことは1度もありません。もしかしたら私の性格や考え方を上長が汲んでくれて、私が不満に感じないようなマネジメントをしてくれていたのかもしれません。

これもたまたまかもしれませんが、一緒に昼食を共にしていた同僚とは会社を辞めてからも繋がっていてやり取りしたりすることが多いです。昼食を共にしているうちに仲良くなるのか、仲が良いから昼食を共にするのか、どちらが真であっても、昼食を共にすることが悪い結果をもたらすことはないように私は経験から思います。意識したことはなかったのですが、本章を読んでいて、そういえばと気付いた次第です。

当然、著者もこのマネジメント法は難しく、うまくやるにはある種の深い対人スキルも要求されると述べています。そして、多くの職場では、場当たり的な、日により人により変わる「何でもあり」の方法でマネジメントされているという言葉で締め括っています。大企業では同じ会社でもマネージャーによってマネジメントが大きく異なるのが容易に想像できます。

第10章 コンピュータサイエンスの学生へのアドバイス

いくつかあるアドバイスの中で私が関心をもったもののみを紹介します。

卒業するまでに文章の書き方を学ぶこと

最も大きな権力や影響力をもつプログラマは、明確に、説得力をもって、やすやすと書き、話せる人間だ。

まぁまぁのプログラマと優れたプログラマの間にある違いは、アイデアについてコミュニケートできるかどうかという点にある。他の人を説得することで、彼らは力を得るのだ。

日記やウェブログをつけはじめるといい。書けば書くほど、書くのは楽になる。そして書くのが楽になれば、もっとたくさん書けるようになるという、プラスのサイクルができる。

私はいま「書くこと」そのものを再考しています。再考というほど、過去にちゃんと考えたことがあったのか問われると曖昧です。しかし、意識的に書く機会を設け、書く時間を割いて何かを考えるようにしています。あるとき、生産性の低い開発チームをみていて、ふと思ったことがあります。

よいプロダクトはよい開発文化から生まれる。よい開発文化は書くことから醸成されていくのではないか?

私は、いくつかの会社やチームで課題管理システムの利用を推奨し、日々のやっていることをチケットに書くことを促してきました。そして、自然に課題管理システムを使いこなす開発者が半分ぐらいしかいないという現実をみてきました。ここでは書かない人たちの理由は追求しませんが、情報の一元化や情報共有の文脈から書くことのメリットは大きいです。情報の非対称性が組織にとって弊害があることを多くの人が実感しているはずです。それでも書かない人たちはいるのです。

経験則では、書かない人たちと技術の多寡は相関がありません。そして、いま思い返すと、書かない人たちはチームで孤立しがちになります。これは周りが避けているわけではなく、その人は何をしているのか、何を考えているのかわからないので必然的に協働する機会が減り、結果として接する距離が縮まらないせいではないかと思います。

口頭と文章によるコミュニケーションでは次の特性があります。

  • 口頭: 同期的で複雑ではない内容に対して数人程度でしか成り立たない
  • 文章: 非同期でも可能で、複雑な内容も不特定多数へ伝えられる

つまり、口頭によるコミュニケーションコストはとても高いということです。そのため、チームのメンバー間で毎日やっていることや業務で考えていることを口頭で双方向に共有することは現実的にできません。例えば、それを課題管理システムのチケットにコメントとして書き込むのであれば、それぞれのメンバーの余裕のあるときに読めるのです。無論、読むかどうかはメンバー次第ですが。

その日々の積み重ねが中長期で開発の生産性をあげたり、チームの協調性を高めていくのに役立ちます。そのためにはメンバーが自律的に書ける必要があります。

もう1つ書くことのメリットをあげてみます。

後藤貴子の米国ハイテク事情Alan Kay 氏へのインタビューの内容です。その中で次のことを発言しています。

過去1世紀の電子技術のほとんどは退行的だ。というのは電子技術の多くは書くことよりオーラルコミュニケーションを奨励するからだ。昔、人々に読み書きを強いた多くのものは今は存在しない。楽しみのために読まなければ、恐らく必要になったときには読む鍛錬が足りていないだろう。書くこともどんどん不要になっている。将来はもっと、コンピュータが、“学ばないこと”の言い訳になるかもしれない。米国の多くの学校は、子供がGoogleで何かを見つけコピーすると、それで学んでいると思っている。しかし私は、子供がそれについての作文を書かない限り学んだことにならないと主張している。作文は思考を組織化する。単に博物館の展示物を集めるだけではない。しかしほとんどの学校はその違いを分からない。

氏は「作文を書かない限り学んだことにならない」と主張しています。書くことは記憶の定着とも関連するので学びの質を高めるように読めます。もし学んだことを自分の言葉で書けないのだとしたら、その内容について理解が欠けていることを自身で容易に判断できます。

これは私自身への戒めとして、このブログのアーカイブ数をみると、2009年から年間の記事数が減っていっていることがわかります。この理由の1つに、勤めていた企業のテックブログや他のプラットフォームで記事を書いたりしていたこともあげられますが、近年、私は書くことを軽視している傾向がありました。学びが減っているのと怠惰になっていることの両方あると考えています。

卒業するまでにミクロ経済学を学ぶこと

ミクロ経済学はビジネスで重要な理論すべての基礎となっている。需要と供給とか、競争優位とか、NPV とか割り引きとか限界効能について知らなければ、ビジネスの仕組みが全然理解できないからだ。

マクロ経済学は、当たっているよりもはずれていることの方が多い。スキップしてよい。

ビジネスの基礎を理解しているプログラマは、理解していないプログラマよりもビジネスにおいてずっと価値が高いからだ。

私自身、いま会社の経営も考えたりするので経済の基本的なことをもっと昔に学んでおくべきだったと振り返っています。著者によるとミクロ経済学より先の経済学はどんどん悪くなっていくのでやらなくてよいとのことです。本節を読んで入門本を読んでみようと思いました。後述する第34章でソフトウェアの価格づけを考察するときにミクロ経済学の用語も出てきます。

仕事がみんなインドに行ってしまうと心配するのをやめること

第1にビジネスの現在の流行に基づいて職を選択するというのはばかげている。第2に、仮にすべてのプログラミングの職がインドや中国に行ったとしても、プログラミングというのは、ビジネスプロセスエンジニアリングになる。第3に、これは信じてもらいたいのだが、非常に優れたプログラマというのはアメリカでもインドでもすごく不足している。

オフショア開発 - Wikipedia によると、1970-1980年代ぐらいにオフショア開発という移転が始まったそうです。2000年代はオフショア開発する企業が増えてきて、先進国でプログラマーの仕事がなくなるのではないか?という話題もあったように私も思います。しかし、いまはすべての業界の会社がソフトウェアを活用するようになった結果、プログラマの需要はまだまだ衰える様子はみられません。日本においても当面、プログラミングのお仕事は売り手市場になるのではないでしょうか。

第15章 ユーザビリティがすべてではない

そこには (少なくともユーザビリティのプロには) 恐ろしい一片の真実があった: 人々が本当に欲する何か本当にすごいことをやるアプリケーションであれば、無惨なほど使いにくかったとしてもヒットしてしまうのだ。そして世界で最も簡単に使えるアプリケーションであっても、みんなが望むことを何もしないのであれば失敗することになる。

多くの場合、ユーザビリティは実際オプショナルだということだ。

著者の会社は課題管理システムを開発しているので業務アプリケーションに近いパッケージベンダーになります。時代が大きく異なるので当時は見た目や操作性よりも機能の方が重要視されたように私も思います。バックエンドの機能がビジネスの差別化に直結していました。

ちなみにユーザービリティとユーザー体験 (UX) とは別の概念であると次の記事で説明されています。ユーザビリティはソフトウェア側の視点からの、特定のユーザーや用途において使いやすいことを指しているそうです。

ユーザビリティは「モノが持つ品質的特性」(実用的品質)であり、UXは「人が体験する感性的品質」であると言えます。

www.cresco.co.jp

本章で著者が言及しているのは、ソフトウェアデザインの次の段階で、ユーザーインターフェースを正しく作ったら、ソーシャルインターフェースのデザインの方が重要であると言及しています。当時はソーシャルインターフェースのデザインについて系統だった学問はなく、新しい分野なので試行錯誤しているように読めました。

FogBugz では多くのデザイン上の決定が、1人で使っても有用であるようになされている。そしてチームの他のメンバにもだんだんと広まっていくのを促すようにデザインされた機能がたくさん組み込まれている。

FogBugz (課題管理システム) の顧客で、バグトラッキングシステムを全然使っていなかった顧客に FogBugz を導入して常用するようになった事例が紹介されていて、利用者がチームで仕事をするやり方が変わったことによると説明されています。ここでいうソーシャルインターフェースというのは、SNS のソーシャルではなく、チームのメンバー同士が協調する上で必要な機能やデザインを指しているように思います。

当時から10年以上経ってチームで協調して使うソフトウェアは進化したのでしょうか。私からみて課題管理システムに限って言えば、大きな変革があったようにはあまりみえないですね。

第20章 エビデンスベーススケジューリング (EBS)

途中まではアジャイル開発で言うところのベロシティを計測して、その値から見積もりの精度を上げる話しであろうと読み進めていました。課題を小さいタスクに分割し、個々のタスクの見積もりと実際の作業時間から速度を算出します。見積もりが正確であれば、速度は 1.0 に近付きます。例えば、見積もりより2倍の時間を要すれば、速度は 0.5 となります。見積もりと実際の作業時間がずれるのはよくあることなので速度の値がバラけることになります。ここでおもしろかったのが、速度をランダム値として、実際にかかる作業時間をモンテカルロシミュレーションで複数生成して、多少のズレがあってもどのぐらいの範囲の日程で終わりそうかの分布を確率的に求めていくというシミュレーション手法です。

スクラムなどでは次のスプリントで何ができるか、その達成に全力を尽くすというスタイルなのでスプリントを5回やれば何ができているかということは分かりません。著者の提案する EBS はちょっと先の未来を開発者が勘と経験で見積もるより信頼できる予測をモンテカルロシミュレーションで算出するといったものです。私は課題管理システムでこういった機能をみたことがありませんでした。プロジェクト管理の文脈ではいくつか記事が検索にヒットするのでこういった機能があるのですね。見積もりの精度が悪いチームではやってみるとおもしろいかもしれません。

その他、個人的に納得感が高いものが自分のスケジュールに次のためのバッファを用意しておくと述べているところです。

  1. 新しい機能のアイディア
  2. 競合への対応
  3. インテグレーション (他メンバーの作ったものと協調して動くようにする作業)
  4. デバッグのための時間
  5. ユーザビリティテスト (および、その結果を製品に反映させる)
  6. ベータテスト

もしかしたらパッケージベンダーだからこそ可能なバッファなのかもしれませんが、アリエルでも近いスケジュール管理をしていました。イテレーションにおける Feature freeze までの1.5ヶ月に対して必須機能を開発者に割り振り、それらを実装すれば、個々の開発者が (バックログから) 好みの新機能を自由に実装してよいというやり方でした。私の場合、2-3個の必須機能、2-3個の好みの機能、合計で5個前後ぐらいの新機能を実装していました。私の記憶では、3年間で開発チームが必須機能を期間内に実装できなかったのは1つだけだった気がします。つまり3年間でプロダクトとしての機能開発の遅れはほとんどなかったと言えます。

精度の悪い見積もりがためにスケジュールの引き直しを何度もやっているプロジェクトをみかけたことがあります。スケジュール調整のための管理コストもかかってしまうので適切なバッファの持ち方もマネージャーやプログラマに一定の経験を要求するのかもしれません。

第27章 バイオニックオフィス

開発チームのマネージャーはいいオフィス空間がどういうものであるかを知っているが、マネージャーの権限でオフィスを引っ越ししたり改装したりできるものではないのでどうすることもできないものだと考えていると述べられています。著者は経営者なので自分の会社のオフィス空間をいいものにしたいというこだわりが伺える内容でおもしろいです。建築士にブリーフ (ソフトウェア開発で言う要件) として提示した内容が次になります。

  1. 1人1人にちゃんとドアの付いた個室があることが絶対条件
  2. コンセントがたくさん必要、プログラマが新しいおもちゃを机の上でつなげられる
  3. データケーブル (電話、LAN、ケーブルテレビ、警報、その他) を床を這い回らずにつなげられる
  4. ペアプログラミングが可能であること (L字型の大きい机を用意する)
  5. 遠くのものを眺めて目を休められるよう窓を設け、ディスプレイを壁に向かって置いてはいけない
  6. オフィスはそこで時を過ごすのが快適なたまり場のような場所であるべき

現代ではデータケーブルの接続インターフェースはなくてもよさそうですね。一定規模以上の従業員が働く会社では個室は幹部のみでしょうし、最近は社員間の協調を優先してオープンなオフィス環境を重視しているイメージが私にはあります。たまり場について、日本人は休憩用途でたまたまそこにいる人たちとコミュニケーションを取ったりすることはあまりないように思います。それよりも、ちょっとした打ち合わせや小さい勉強会に、普段の会議室とは違う、ややくつろいだ雰囲気で行えるスペースがあるとよいように私は思います。

私はいまシェアオフィスを借りています。ドアのある個室で8割ほど満足しているのですが、2年近く使っていて、唯一不満だと思い始めたのがまさに窓がないことでした。窓がないと1日の時間の経過、天候の変化、季節の移り変わりに疎くなります。直接、業務には関係ありませんが、外から受ける刺激が少なく自然な気分転換にならないように感じるようになりました。次に引っ越しするときは窓のある部屋を借りようと考えています。

フィリップ・グリーンスパンは、はっきりこう言っている。「会社の成功は、ある部分までプログラマが実質オフィスに暮らすようになるかどうかにかかっている。オフィスが平均的なプログラマの家よりも素敵な場所である必要がある。そうする方法は2つある。すごくみすぼらしいアパートに住んでいるプログラマを雇うというのが1つで、もう1つは素敵なオフィスを作るかということだ」

Managing Software Engineers

学生時代に研究室に入り浸って過ごした人たちには馴染みがあるかもしれません。過去、私もオフィスに泊まり込みで働いたこともあったので環境がよいに越したことはないというのは理解できます。私は自宅で仕事をしない人だったので、自宅とオフィスの目的を明確にわけていました。一方で IT 業界ではリモートワークが普及しつつあり、私の周りではオフィスよりも自宅環境が快適で満足しているという友だちも少なからずいます。自宅とオフィスのどちらがよいか、個人差があるかもしれません。

第32章 心に残るカスタマーサービスへの7ステップ

ソフトウェア開発の本でカスタマーサービスについて言及している内容を私はあまり読んだことがないように思います。それだけで希少価値があるように感じますし、実際に内容も示唆を与えるものでした。

  1. 問題はすべて2通りの方法で解決する

    • テクニカルサポートは開発チームと接触できることが必須、つまりテクニカルサポートはアウトソースできない
    • やがて一般的な単純な問題はすべて解消され、奇妙で難しい問題だけが残る
      • 新人によくある10種類の対応方法を教えておけばいいわけであはない、デバッグの仕方を教える必要がある
      • アリエルでもサポート問い合わせはリリースマネージャーやコンサルが一次受けして、わからないときはエスカレーションされてプロダクトの開発者がシフトで調査していた
  2. ホコリを払うように勧める

    • ユーザーに回答するときに言い方を工夫して相手を怒らせないようにするという話し
    • キーボードが効かないというユーザーに「ちゃんとつながっていますか?」と聞くと、ユーザーは確認もせずにバカにしているように感じる
      • 「接点のゴミがついていると接続が弱くなる。接点のゴミを吹き払ってからつなぎ直してもらえますか?」というと、自然にやってもらえる
  3. 顧客をファンにする

    • 顧客が問題を抱えている時、それを解決してあげたなら、彼らは始めから問題なんかなかった場合よりもいっそう満足を覚える
      • 私も過去にトラブル対応をして逆に顧客の信頼を得たことがあるのでこの考え方は支持します、ピンチはチャンス
  4. 責めを負う

    • 鍵のトラブルで「私のミスです。」と行った鍵前屋に対して著者は怒りの感情がなくなってしまった話し
    • ミスを認めない姿勢や態度そのものにさらに腹が立つという話しに読める
  5. 言いにくい言葉を覚えておく

    • 顧客からクレームがあったときの返す大事な言葉は覚えておいて、それを言う練習をしておく
    • 例えば「私のミスです」、「申し訳ありませんでした。お代は結構です。」
    • 心から言っていることが伝わる必要がある。だから練習をするのだ
  6. 操り人形の練習する

    • 怒れる顧客の相手を感情的に切り抜ける唯一の方法は、彼らが怒っている相手は自分ではないのだということを認識することだ
    • 怒られているのは自分という自然人ではなく、会社という法人だと置き換える
    • 操り人形のように個人の感傷をなくして謝罪すればいいという話しに読める
      • 私の経験では、過去に謝り方の下手な人は自分は悪くないと顔に出ていて顧客を逆に怒らせてしまう人がいた
      • 当事者意識をなくさずに操り人形になるスキルは言うほど簡単ではないかもしれない
  7. 強欲ではどこにもたどりつけない

    • 求人広告サービスで質問なしの90日間返金保証をしてうまくいった話しに読める
    • クレジットカードで支払ったときに払い戻しに応じないとき、銀行に電話して払い戻させることができる。これをチャージバックと呼び、事業者側はチャージバック手数料を支払うことになる、たびたび起きると手数料が引き上げられることになる
    • 顧客が払い戻しを要求したら結果は同じになるのでもめずにすぐ払い戻しに応じるという話しに読める
  8. (おまけ) カスタマーサービスの人たちのためのキャリアパスを用意する

    • テクニカルサポートにはデバッグ能力を要求するため、資質の高い人を配置する必要がある
      • アリエルではプロダクトの開発者が交代で担っていた
    • 資質に優れた人がテクニカルサポートをしているとその仕事に飽きてしまう
    • Fog Creek では3年間のマネジメントトレーニングプログラムの最初の年にだけやる仕事になっている
      • キャリアパスの途上にいる野心的で頭のいいギークが顧客と話して問題解決するきっかけになる
      • 平均よりもずっと高い単価を支払うことになるが、はるかに大きな価値を引き出している

本章の内容はいまでも通用するように私は受け取りました。カスタマーサービスの問い合わせ内容を重視して、スキルの高い人を配置し、さらにその人たちのキャリアパスも用意しておくということをできているパッケージベンダーは少ないのではないでしょうか。テクニカルサポートは習熟するとキャリアアップのために辞めてしまうイメージが私にはあります。

第34章 ラクダとおもちゃのアヒル

パッケージソフトウェアの価格付けについてミクロ経済学の考え方を応用しながら論理的に考察していきます。著者がソフトウェア開発者も学んだ方がよいと助言するミクロ経済学の勉強にもなりますし、ユーモアのあるの文章で楽しめました。どういった属性の顧客にいくらで販売するのが利益を最大化できるかを次の手法を使って求めていきます。

  • 需要曲線
  • 消費者余剰
  • セグメント化
  • 正味現在価値 (NPV)

最終的にどういう価格付けになるのか。これから読む方のために秘密にしておきます。こういった考察を読むことで、ビジネスにおける数字の見方や考え方を学ぶ必要性も理解でき、経済学への興味へとつながるように思いました。本章を読み終えた後に気になって次の記事も読みました。

blog.livedoor.jp

所感

一通り読んで、私が関心をもった箇所を書き出してみました。その過程でアリエルのよかったところが本書でも言及されている内容だったりして思い返すことも多々ありました。2000年代に書かれた本でも、学ぶべきところや時間が経ってもあまり変化がない内容もあります。私にとっては温故知新ということばがぴったり当てはまる内容でした。

エラスティックリーダーシップ――自己組織化チームの育て方

最近出版された本ではありませんが、じっくり読む機会があったので簡単に所感をまとめてみます。購入したのは約1年前でした。それから積ん読していて、読み始めてからも時間のあるときに少しずつ読み進めて1ヶ月ぐらいかかりました。

www.oreilly.co.jp

買っておくといつか読む可能性がある。

読まないよりは少しでも読んだほうがよい。

一通り読めてよかった。

。。。

そんな所感は誰も期待してないでしょうから、少し振り返りながら私にとって関心のある内容をまとめてみます。私はリーダーや管理職に就いたことがないので本当の意味では、本書のリーダーの難しさや悩み、葛藤などは理解できていないと思います。

一方でメンバーの立場でリーダーを補佐してきたことや、いくつかの企業やチームでそれぞれのリーダーに接してきたこと、自分にとって働きやすかったリーダーなど、やはり、本書を読んで共感できるところは多々あります。

そして、自分がリーダーとして振る舞うことが求められるとき、やはり、本書は人それぞれのリーダー観を獲得するヒントにもなるでしょう。

チームの自己組織化

本書でのキーワードの1つに「自己組織化」があります。聞いたことがあるような単語ですが、そもそも自己組織化とはどういう意味だろう?と思ったときに私は答えられませんでした。

パターン形成の仕組みを理解するために、物理学、化学、生物学、情報科学などに広く用いられる概念。無秩序状態の系において、外部からの制御なしに秩序状態が自律的に形成されることをいう。ここで「外部からの制御なしに」とは、外部から細かく手を加えてパターンを作成するような作用がないということを意味する。

自己組織化とは - コトバンク

いくつか説明の記事を読んで、コトバンクの説明が私にはわかりやすかったです。科学の分野にまたがって用いられる概念であるという。系をシステム、秩序をパターンやルールのように読み替えるとエンジニアリングの概念とも合致しそうな雰囲気はします。

それではチームの自己組織化とはどういう意味でしょうか。ちょうどその記事がありました。

www.infoq.com

この記事によると、アジャイルソフトウェア開発宣言 (Agile Manufesto) の一節に次の文言が出てきます。

最良のアーキテクチャ・要求・設計は、自己組織的なチームから生み出されます。

アジャイル宣言の背後にある原則

記事内では、単なる個人の集まりとしての共同作業グループと、その宣言で意図された自己組織化チームとの違いを、様々な特徴、モデル、要素などから説明しています。私がざっと読んだだけでは理解できそうにありません。私の中ではまだ言語化できていないので、ここではチームの自己組織化とは尊いものだとしておきましょう。そのぐらいの安易で、なんら初学者の理解を助けない表現で留めておきます。そして、チームの自己組織化は、簡単には成らず、成ったとしても永遠に続くものではなく、メンバーが相互に作用して目的を果たしていく形態のようです。

リーダーシップのスタイルとチームのフェーズ

チームには異なるリーダーシップを必要とするフェーズ/状態がある。本書では次の3つの関係を図解で表しています。

  • 指揮官が必要なとき: サバイバルフェーズ
  • コーチが必要なとき: 学習フェーズ
  • ファシリテーターが必要なとき: 自己組織化フェーズ

図では「モード」と記載されているが「フェーズ」とも呼ぶと説明されている箇所があるのでモードとフェーズは同じ意味で捉えてよさそうです。

f:id:t2y-1979:20210727145652p:plain
3つのチームのフェーズ

サバイバルフェーズ

本書では、チームに学習する時間が十分にない状態を「サバイバル」と定義している。リーダーはメンバーが学ぶ時間をとれるよう、サバイバルモードから抜け出すための戦略を練る必要がある。そういった状態では指揮統制型のリーダーシップが有効である。

学習フェーズ

十分なゆとり時間があり、その時間を使って学習や検証を行っているなら「学習」フェーズにいると言える。このフェーズにおけるリーダーは、メンバーに対して自分たちの問題を自力で解決できるように教え、挑戦させ、自己組織化チームへと育てることが目標となる。そのため、コーチ型のリーダーが必要になる。

自己組織化フェーズ

リーダー/メンバーともに何の心配もなく、携帯電話やパソコンの電源を切り、数日間仕事を放置できる状態であれば、自己組織化フェーズと言える。このフェーズにおけるリーダーは、ファシリテーターとなってその状態を維持すること、そして現状を処理するチームの能力に注意を払うことが目標となる。

フェーズ間の移動

チームの状態によってはリーダーシップが異なること、チームに発生するイベント、例えば、新人がチームに入ってくる、目標の期限を変更するといったイベントが発生したときにフェーズが移動することもありえると説明されている。

f:id:t2y-1979:20210727151023p:plain
チームのモードを検出する

スタイルやフェーズに関する所感

リーダーシップのスタイルとフェーズにも「自己組織化」というキーワードが出てきます。他のフェーズと比べ、リーダーは自由時間をもち、メンバーは自分たちで意思決定を行い、チーム自身で問題解決できるという、尊い状態であることが伺えます。

本書では、それぞれのフェーズについて章を設けてより詳細に説明されています。その中で私が印象に残った5章の学習することを学ぶについて取り上げてみます。

学習することを学ぶ

学習モードとは、チームがゆとり時間を持ち、それを新しいスキルの集中的訓練にあてている状態と定義できる。

本書を読んで私がもっとも関心をもったことの1つに学習には「谷」があると説明されていたことでした。

進捗には2つのタイプがある。

  • 学習の進みが遅く安定している高原。
  • 学習が大きく飛躍している峰。

f:id:t2y-1979:20210727152609p:plain
学習の変化と谷

さらに引用します。

私が思うに、学習の真の力というのは、次の単純な事実を悟ることだ。谷は最終的には終わって、あなたは新しい知識と共に飛躍する ことになる。このことを知ってい れば、不可能だと思うことだって、やり始めることができる。

そして、自分が谷に直面している可能性があるのは次のような状況にあると言う。

  • 面目や尊厳、お金や友人を失う可能性があるために、一歩を踏み出すのに勇気がいる。
  • 違和感を感じている。
  • 自分を変えるかもしれないこと、あるいは、自分の世界や仕事、関わる人々への理解の仕方を変えそうなことがある(が、これを事前に知るのは難しい)。
  • 新しいスキルを獲得しなくてはならない。

真の学習は谷によってもたらされる。谷底は不快で、時折イライラさせられる。メンバーが楽しくなさそうなら、彼らをうまく安全地帯の外に出せている良い兆候かもしれない。メンバーから抵抗を感じたなら、それはあなたがうまくチームを操縦できている証拠だ。

余談ですが、先日、寺田さんの podcast に出演した際にも本書の学習の谷について少し話しました。

podcast.terapyon.net

私はずっとバックエンドの開発に携わってきて、最近フロントエンドの開発に携わりました。その過程で自分の培ってきた経験や知識と明らかに考え方の異なる設計やアプローチに対して、大きな違和感を受けつつ、それを言語化できない自分自身のもどかしさに葛藤や逡巡を覚えました。

決して自身の経験や知識を正しいと主張して新たな開発スタイルを否定する気持ちはありませんでした。しかし、設計とはトレードオフを強いることもあるため、この設計ではこういうケースに対応できないとか、拡張するときにこういったデメリットがあるといった、大小あれども自分の価値観との違いをうまく言語化できずにもがいていました。

手伝い始めた当初はアンラーニングも大事だろうと考え、盲目的に同僚の意見や設計を受け入れながら開発を進めました。同僚はあまり設計やアーキテクチャ言語化せず、意図も説明しなかったので、違和感があっても指摘するのが面倒になって放置するといったときもありました。

そのときの楽しくない気持ち、自身の行動に自信がもてない気持ち、そういった感情の振れ幅とモチベーション管理をうまくできなくて自己嫌悪に陥ったときもありました。思い起こせば、これまでも新しいことに挑戦するときは同じような、うまくいかなくて挫折感を味わうというのは何度も経験していました。しかし、それを「学習の谷」という概念では捉えていませんでした。本書で谷という概念を知ることで少し救われたように私は感じました。

真の学習というのは、以前より生産性が落ちたり、違和感を感じて不快なものであると考えることにより、いくらか新しい挑戦への不安を軽減する術になるように私は思います。

第7章「メンバーを成長させる」にもリーダーがメンバーを指導するとき、メンバーにとって真の成長機会であるかを見分ける項目として、学習の谷にあることを確認するときの項目と同様に次をあげている。

  • 恐怖があること
  • 初期の不快感や不満があること

影響パターン

メンバーの主要な振る舞いを変えるように影響を与えたい場合の6つの影響力について次の説明があります。当初、この影響力のマトリクスの説明を読んだときはあまり内容を理解できていませんでした。

本書の後半は、様々なリーダーが1章ずつエッセイを綴っています。そのエッセイに対して著者が分析してコメントを書いています。そのコメントを読んでいると、このエッセイは影響パターンのこれとそれを示唆しているといった説明が度々でてきます。

より具体的な事例を影響パターンに分類することで私の理解が深まった気がします。

f:id:t2y-1979:20210727161500p:plain
影響力のマトリクス

現場リーダーのための 6 つの原則 平鍋 健児

過去に平鍋さんの記事をいくつも読んで私は大きな影響を受けています。その中で「名前づけ」の節がおもしろかったので紹介します。

気づきによって得た知識に名前をつけましょう

問題を解決する方法には、「ブレークダウンによる方法」(Divide and Conquer)と、もう一つ、「名前づけによる方法」(Name and Conquer)があります。

プログラミングで最も難しいことの1つに、やはり名前づけがよく言われます。私は初期の開発時にしょっちゅう名前を変えます。

名前を知ったことで人間の認知に影響が出たエピソードとして「ヨシュアツリーの原則」も紹介されています。

www.atmarkit.co.jp

まとめ

読者の経験や立場によって本書の興味や関心をもつ箇所は変わるでしょう。また時間が経って、自分の状態の変化によっても本書から受け取る内容は変わるかもしれません。

さて、冒頭でチームの自己組織化とはなんぞや?という問いに、私は尊いという言葉で表現していました。本書を読むと、自己組織化とはこういった内容かなと、自分なりの解釈や理解が、読む前よりは進むように思います。そして、その解釈や理解に絶対というものもないように私は考えています。

私にとっての、チームの自己組織化というのは、チームが自律的に課題を解決していくための責任と権限をもち、それが開発文化として十分に根付いており、リーダーもメンバーもその役割を担う上での価値観が共有されている状態であると考えます。

できる 仕事がはかどるPython自動処理 全部入り。の第5刷が決定しました

昨年、ビジネスパーソン向けの Python 本を執筆したことを書きました。

t2y.hatenablog.jp

おかげさまでまだコツコツと売れているようです。本書を購入していただいた方々、ありがとうございます。

このたび、第5刷の重版が決定しました。

【第5刷】7月17日(金)重版出来

book.impress.co.jp

出版してから1年ほどが経過し、今回の重版で発行部数が1万部を突破しました!

ビジネスPythonを学ぶ会の活動と slack の紹介

本書の読者サポートも兼ねて私が運営しているコミュニティがあります。

新型コロナウイルス感染防止の意図もあり、勉強会をオンラインで開催するようにしました。世の中的にオンラインで勉強会に参加することが一般化したせいか、オフラインで開催していたときよりも多くの人が参加してくれるようになりました。このままずっとオンライン勉強会を継続しようかと最近は考えています。

このコミュニティは難しいことを扱うわけではなく、またすぐに業務で役に立たないかもしれませんが、コミュニティとして長く続けられることだけを目標にしています。Python で困ったときに気軽に相談できる場になればいいなと思います。

bizpy.connpass.com

本書の対象読者と同様、Python 入門を終えたばかり初学者、さらに IT エンジニアではない次の方々を対象としています。

いまのところ、月1回の勉強会を開くことを予定しています。オンラインでのコミュニケーションが取れるように bizpy.slack.com を開設しています。興味のある方はこちらの 招待リンク からご参加ください。

コミュニティの趣旨の詳細については次のスライドに整理したので興味がある方はこちらも参考にしてください。

docs.google.com

次回からは Web API/SDK を扱う勉強会を何回かのシリーズで開催する予定です。本書では扱っていませんが、勉強会の参加者から Twilio というサービスがあると教えていただいたので次回は Twillo の API/SDK を取り扱います。

直接、本書にない内容であっても同じレベル・カテゴリに属する話題であれば勉強会の中で積極的に扱っていくつもりです。コミュニティなのでそういった参加者とのやり取り、そのとき流行っているもの、世の中の変化にあわせた話題などを提供できればいいなと思います。

私自身、普段の業務で扱っているビジネスや技術分野はとても狭いので参加者からそういった情報を教えてもらえることはありがたいものです。普段、接点がない人たちの視点や話からお互いに示唆をうけるような取り組みをさらに進められればと思います。

プログラミングできる人が増えること自体がいまの世の中にとっては良いことだろうと私は信じているので今後もそういった取り組みへのサポートをしていきます。

まとめ

プログラミングのスキルを身につけるには、一般論として、たくさんのコードを読んでたくさんのコードを書くことで習得できます。一朝一夕ではならず、日々の生活や業務に無関係だとなかなか行動に移すのが難しい場合もあるでしょう。

勉強会などは直接のスキルアップには結びつかないものの、スキルを学ぶきっかけ作りとして役に立つ場合があります。私自身、技術コミュニティへ参加しながらプログラミングを学んできました。

本書の出版をきっかけに「ビジネス x プログラミング」というテーマを考えながら1年ほど経過しました。しかし、まだまだ試行錯誤で今後の展望もみえていません。それでも勉強会を継続してきたことで少しずつ改善がみえ、ノウハウやコンテンツも積み上がってきました。いまはこのサイクルを継続することが次につながるように捉えています。

リファレンス

t2y.hatenablog.jp

Pythonハッカーガイドブック -達人が教えるデプロイ、スケーラビリティ、テストのコツ-

マイナビ出版 さんから献本していただきました。ありがとうございます!

book.mynavi.jp

本書は初学者向けではなく、Python である程度プログラミングができるようになった後にスキルアップするための、中級者向けの書籍になります。昨今の流行りから言うと、例えば、データサイエンスに関わるデータサイエンティストやプログラマーがより実践的で効率のよいプログラムを書くことや、Python を使ったプロジェクトをうまくまわすための手助けになるでしょう。

本書では Python プログラミングでよく知られたプラクティス、ドキュメント作成、パッケージング、テスト、パフォーマンスの最適化とアーキテクチャなどが紹介されています。

著者の Julien Danjou 氏は、OpenStack という、巨大な Python プロジェクトのコントリビューターであり、チームリーダーを務めているようです。冒頭の「はじめに」のところでは OpenStack は900万行もの Python のコードで構成されているとあります。

単なる Python のテクニックのみを集めた書籍というわけではなく、著者の経験による、(ある程度の規模の) Python プロジェクトを通して Python をどのように活用してきたか、あるいは活用すればよいかの示唆を与えるような、目次からもそういう意図が伺えます。

本書からいくつか抜粋して私の興味・関心のある章を紹介します。私はこれまで何冊か Python の書籍を読んできましたが、その中で他の書籍ではあまりみたことがない内容を取り上げてみます。

第4章 タイムスタンプとタイムゾーンの処理

頻繁に扱う処理ではないものの、日時間の差を求めたいときなど、タイムゾーンはどうやって扱うのだったかな?と検索することが私は稀によくあります。もう同じことを10回以上は調べた気がします。本章でタイムゾーンをどう扱えばいいのかを学ぶことができます。

余談ですが、日時を扱う処理は本質的に複雑です。

自分が言えるのは、日時処理はそもそも本質的に複雑だという経験です。ざっと思いつくだけでも日時処理が複雑になる要因として次のような思いつきます。

  • そもそも一貫性がない(月ごとに日数が異なる)
  • 様々な基数(12進数、60進数、7進数?(曜日))
  • 何の法則もない祝日
  • 何の法則もない年号
  • 複雑さに輪をかけるタイムゾーン
  • 複雑さに輪をかけるうるう年
  • 複雑さに輪をかける夏時間

Java8の日付および時刻処理(Date/Time API(JSR-310))の紹介と利用指針 | ありえるえりあ

プログラミングを教えるときなど、本質的複雑さとはなんぞやを説明するのに日時データを扱う処理をイメージするとわかりやすいので説明にもよく引用します。業務においても、こういった法則性もなく、なんらかの背景により条件が課せられるといったことは頻繁にあります。

閑話休題。まず Python の標準ライブラリで datetime オブジェクトを取得するとタイムゾーンの情報はもっていません。

>>> from datetime import datetime
>>> datetime.utcnow().tzinfo is None
True

タイムゾーンの情報を扱うには python-dateutil というサードパーティのライブラリを追加でインストールして使うのが一般的だと思います。

>>> from dateutil import tz
>>> timezone = tz.gettz('Asia/Tokyo')
>>> datetime.now(tz=timezone)
datetime.datetime(2020, 5, 29, 20, 31, 26, 444650, tzinfo=tzfile('/usr/share/zoneinfo/Asia/Tokyo'))

例えば、サマータイムが終了するときはローカル時間では同じ時間が2回やってきます。1回目の時刻と2回目の同じ時刻を判別できるように fold という属性が Python 3.6 以降で追加されています。うろ覚えで見かけた気はしますが、自分でコードを書いたことがなかったので今回初めて fold を使ったコードを学びました。

>>> berlin = tz.gettz('Europe/Berlin')
>>> confusing = datetime(2020, 10, 25, 2, tzinfo=berlin)
>>> confusing.replace(fold=0).astimezone(timezone)
datetime.datetime(2020, 10, 25, 9, 0, tzinfo=tzfile('/usr/share/zoneinfo/Asia/Tokyo'))
>>> confusing.replace(fold=1).astimezone(timezone)
datetime.datetime(2020, 10, 25, 10, 0, tzinfo=tzfile('/usr/share/zoneinfo/Asia/Tokyo'))

日本はサマータイムがないのでこういった処理を実装することは滅多にないでしょうが、知っておくと海外の開発者と一緒にお仕事をするときに役立つかもしれません。

第9章 AST、HYLisp ライクな属性

私の記憶では ast モジュール を説明している Python の書籍は滅多にありません。私自身、過去に ast モジュールの使い方がわからなくて調べたときがありました。

公式ドキュメントを読んでもモジュール内のクラスやメソッドの説明しか書いていないため、どういった用途に使えるものかよくわからなかったからです。

幅広い分野で利用されている Python の用途からすると、ast モジュールを使う機会は相対的に限定的であると言わざるをえないでしょう。したがって ast モジュールを説明する書籍は稀だと私は思います。

第9章の冒頭では、まさに PythonAST は十分に文書化されていないという背景から本章を設けたことが述べられています。そのため、本章は ast モジュールの使い方から Python の AST オブジェクトの解説も行われています。そして、ast モジュールの典型的な実用例としてコードの静的解析ツールである flake8 を拡張するサンプルコードが紹介されています。

最後に Hy という Python の AST を使って実装された Lisp ライクな言語を紹介しています。著者は Lisp 愛好家のようです。

github.com

Hy の話題は本当におもしろい取り組みだと私は考えていて、この言語は PythonDSL (マクロ) をどう実装するかの、実際に動く事例の1つであるからです。

$ pip install hy
$ hy
hy 0.18.0 using CPython(default) 3.8.1 on Linux
=> (defn hello [name]
... (print (% "Hello %s!" name)))
=> (hello "t2y")
Hello t2y!

Hy の紹介を終えた後にその開発者である Paul Tagliamonte 氏のインタビューも掲載されています。このインタビューも示唆に富んでいて私は楽しめました。Python の AST はプライベートでもパブリックでもない曖昧な仕様であること、Python の実装やバージョン間によって AST が異なる可能性があること、HyPython の相互運用性は双方向にインポートできることから驚くほど高いことなどが述べられています。

現実の業務では、ast モジュールや Python の AST を操作するようなことは、静的解析やコード生成、特殊な事情における最適化など、ほとんどの開発者にはあまり関心のない話題かもしれません。しかし、言語処理系やプログラミングのことをより深く学ぶにはよい取っ掛かりでおもしろい題材になると私は思います。

本書は全体として実践的な内容をまとめたものです。しかし第9章だけは、どちらかと言うと、ハックを刺激する内容になっています。そのため、プログラミングが好きな開発者向けに楽しめる章ではないかと私は思います。

第11章 スケーリングとアーキテクチャ

ページ数は10ページ強と少ないのですが、アーキテクチャの話題を Python に特化した内容で解説しているのがよいと私は思います。プログラミング一般でもてはやされるアーキテクチャがその言語で必ずしもよい選択肢とは限りません。他言語のプラクティスをそのまま導入してもうまくいかないこともあるでしょう。

私はプログラミング言語の特性や、これまでの経緯を踏まえてそのプログラミング言語にあったアーキテクチャを採用するのがよいと最近よく思うようになりました。世の中の流行りを追いかけるよりも、その言語で培われた実績の積み重ねの方が優れている場合があるからです。

本章では歴史的な経緯も踏まえ、次の順番で説明されています。

マルチスレッディングの背景や制限を説明した後に、マルチプロセッシングや asyncio の話題に移っていきます。

asyncio が導入された背景として、それまで存在していた Python の非同期フレームワークやライブラリにおいて、各々における互換性や相互運用性を確保する標準的なイベントループインターフェースを提供することが狙いであったと解説されています。したがって、asyncio はアプリケーション開発者が直接使うというよりも、どちらかと言えば、フレームワーク/ライブラリの開発者が利用するモジュールのようにみえます。

そうとはいえ、asyncio とともに導入された asyncawait のキーワードや ネイティブコルーチン の概念など、アプリケーション開発者にとっても学ぶべきパラダイムはあります。

最後は ZeroMQ というメッセージキューを使って、マルチプロセス間での通信を行うサンプルコードも紹介しています。非同期/並行処理は、人間にとって難しく、なるべく言語機能やフレームワーク/ライブラリの機能を使いつつ目的の処理を実装するのが品質の高いアプリケーションを開発する戦略と言えるでしょう。

第13章 コーディングを減らしてコードを増やす

なんとなくおもしろい章のタイトルですよね。コーディング減らせばコードは減るはずなのに増やそうというわけです。これはどういうことでしょうか?

ある機能を汎用的に使えるようにしたり、ボイラープレート (繰り返し記述するようなものを指す) コードを減らしたりして、応用コードではそれらを再利用することで全体のコーディング量は減らしつつ、応用コードを増やすということを意図したタイトルなのでしょうかね?この章では次の内容を解説しています。

  1. sixを使ってPython 2とPython 3をサポートする
  2. PythonLispのように使ってシングルディスパッチャを作成する
  3. コンテキストマネージャ
  4. attrsを使って決まりきったコードを減らす

これからは Python 2 の話は忘れてよいので six を使う場面はどんどんなくなっていくでしょう。コンテキストマネージャももう十分によく使われていますし、私もよくユーザー定義のコンテキストマネージャーを実装します。また監訳注にも補足がありますが、Python 3.7 で導入された dataclass は attrs からインスパイアされたものです。

masahito.hatenablog.com

消去法というわけでもありませんが、Python 3.4 で導入された ジェネリック関数 を定義するシングルディスパッチについて紹介してみます。導入当初は記事をよくみかけましたが、それから時間が経って最近はあまりみかけない気もします。

第9章でも述べられていたように、著者は Lisp 愛好家のようなので CLOS (Common Lisp Object System) と同様の方法でジェネリック関数をディスパッチする方法を定義しているシングルディスパッチの仕組みを Lisp の考え方と共に Lisp コードも交えて説明しています。Lispジェネリックな関数をどう扱うのかの雰囲気もわかっておもしろいです。

シングルディスパッチと関数のオーバーロード

シングルディスパッチができて何が嬉しいかを私なりに考えてみましたが、簡潔に言語化することは私には難しかったです。そこで考察したことをそのまま書いてみます。

この節では functools.singledispatch を扱う次のようなサンプルコードが紹介されています。これは実践的に意味のない、ただのサンプルコードでしかないのであまりよい例とは言えませんが、比較しやすいようにこのサンプルコードをベースに説明を続けます。

次の場所にここで説明するサンプルコード一式を置いてあります。

import functools

class SnareDrum: pass
class Cymbal: pass
class Stick: pass
class Brushes: pass

@functools.singledispatch
def play(instrument, accessory):
    raise NotImplementedError('Cannot play these')

@play.register(SnareDrum)
def _(instrument, accessory):
    if isinstance(accessory, Stick):
        return 'POC!'
    if isinstance(accessory, Brushes):
        return 'SHHHH!'
    raise NotImplementedError('Cannot play these')

@play.register(Cymbal)
def _(instrument, accessory):
    if isinstance(accessory, Brushes):
        return 'FRCCCHHT!'
    raise NotImplementedError('Cannot play these')

print(play(SnareDrum(), Stick()))
print(play(SnareDrum(), Brushes()))
print(play(Cymbal(), Stick()))
print(play(SnareDrum, Cymbal()))

play(instrument, accessory) という関数の第一引数 instrument によって呼び出す関数をディスパッチするようなサンプルコードです。このサンプルコードを別のアプローチで実装すると次のようなコードになります。関数型プログラミングオブジェクト指向プログラミングで実装の違いはあれど、どちらも同じ機能は提供できます。どちらがよいという話ではありません。

class Instrument:
    def play(self, accessory):
        raise NotImplementedError('Cannot play these')

class SnareDrum(Instrument):
    def play(self, accessory):
        if isinstance(accessory, Stick):
            return 'POC!'
        if isinstance(accessory, Brushes):
            return 'SHHHH!'
        raise NotImplementedError('Cannot play these')
...

def play(instrument, accessory):
    return instrument.play(accessory)

print(play(SnareDrum(), Stick()))
print(play(SnareDrum(), Brushes()))

どちらのコードでも play(instrument, accessory) という同じ関数名を異なるインスタンスからも使えることに意義があります。ここではこのことをジェネリック関数と呼んでいます。後者のコードはポリモルフィズムと呼ばれたりします。

ここでそれぞれのインスタンスが受け取る引数が異なるパターンも考えてみます。説明をわかりやすくする意図で引数の数が異なる場合を考えます。

@play.register(Cymbal)
def _(instrument, accessory, other):
    if isinstance(accessory, Brushes) and isinstance(other, Stick):
        return 'FRCCCHHT!'
    raise NotImplementedError('Cannot play these')

...

print(play(Cymbal(), Brushes(), Stick()))

ジェネリック関数の定義では instrumentaccessory の2つの引数しか受け取っていませんが、 Cymbal に紐づく関数でさらに other という3番目の引数を受け取るように変更してみます。結論から言うと、SnareDrum に紐づく関数では2つの引数を、 Cymbal に紐づく関数では3つの引数を受け取って意図したように動きます。

@functools.singledispatch の公式ドキュメントには型の違いによる、関数のオーバーロード の説明がされているので受け取る引数の数が異なっても動くのはただの実装依存かもしれませんが、ここでは関数のオーバーロードの説明をわかりやすくする意図で引数の数が異なるケースで続けます。

例えば、次のようなコードを考えます。

def add(x, y):
    print(f'{x=} + {y=} = {x + y}')

def add(x, y, z):
    print(f'{x=} + {y=} + {z=} = {x + y + z}')

add(1, 2, 3)
add(1, 2)  # この呼び出しはエラーになる

add() という同じ関数名で異なる引数を受け取る関数を定義したい場合、Python では関数のオーバーロードをサポートしないため、このコードでは後優先で3つの引数を受け取る後者の関数しか使えません。Python に限らず、動的型付け (ダックタイピング) の言語では実行時に型チェックすることから、関数のオーバーロードができるメリットよりも、呼び出し側が引数を間違えてしまったり、引数違いで同じ関数の実装が複数あることによる、コードの見通しの悪さといったデメリットの方が上回るからでしょう。

関数のオーバーロードがなくても、例えば、デフォルト引数を使うことでその目的は果たせます。これは引数が2つでも3つでも意図したように振る舞います。

def add(x, y, z=0):
    print(f'{x=} + {y=} + {z=} = {x + y + z}')

add(1, 2, 3)
add(1, 2)

しかし、デフォルト引数だと、受け取る引数のパターンが増えるとややこしくなるかもしれません。やっぱり関数のオーバーロードっぽいことがしたくなったとしても、例えば、次のようなデコレーターを使って簡単に実装することもできます。

import inspect

class Overload:

    def __init__(self):
        self.namespace = {}

    def __call__(self, func):
        spec = inspect.getfullargspec(func)
        self.namespace[len(spec.args)] = func
        def wrapped(*args):
            f = self.namespace.get(len(args))
            if f is None:
                raise NotImplementedError(f'Not defined for {len(args)}')
            return f(*args)
        return wrapped

overload = Overload()

@overload
def add(x, y):
    print(f'{x=} + {y=} = {x + y}')

@overload
def add(x, y, z):
    print(f'{x=} + {y=} + {z=} = {x + y + z}')

@overload
def add(a, b, c, d, e, f, g):
    print(f'{a + b + c + d + e + f + g}')

このサンプルコードは引数の数だけに着目した関数のオーバーロードのデコーレーター実装ですが、これを汎用的にしたものが @functools.singledispatch という見方もできます。もちろん第1引数の型を使ってディスパッチする機能も使えるわけですが。

シングルディスパッチかポリモルフィズムかという議論は、複数のプログラミングの話題の背景を含んでいるように私は思います。

私は型システムに明るくないので適切な言い方ではないかもしれませんが、プログラマーの設計の考え方によってシングルディスパッチを好むスタイルがあることも理解できます。

余談ですが、私も過去に技術考察の一環で enum の定数固有メソッドを考察したときにオーバーロードの仕組みを実装してみたこともあります。用途によっては、関数のオーバーロードにより、コードの見通しがよくなるケースもあるのではないかと個人的には思います。

qiita.com

シングルディスパッチについての、私の結論としては、コーディングスタイルや設計の好みで使い分ければいいのではないでしょうか。

まとめ

私の興味・関心をもとに他の Python の書籍ではあまり出てこないような話題を取り上げてみました。

著者が Lisp 愛好家なので関数型プログラミングのプラクティスや話題が全体を通して出てくるところも特徴的かもしれません。本稿では紹介しませんでしたが、第8章は「関数型プログラミング」というタイトルですし、上述したように第9章では ast モジュールや Hy を取り上げていました。

Python は手続き型のプログラミング言語ではあるものの、関数型プログラミングの機能 (ライブラリ) もいくつか備えています。関数型プログラミングの考え方やアプローチを学ぶ上でも本書はよい入門になるように思います。

2020-06-13 追記

勉強会の LT で第8章「関数型プログラミング」の一部を紹介しました。

docs.google.com

2020-11-21 追記

勉強会の LT で第9章「AST、HYLispライクな属性」の一部を扱いました。

docs.google.com

できる 仕事がはかどるPython自動処理 全部入り。の第4刷が決定しました

昨年、ビジネスパーソン向けの Python 本を執筆したことを書きました。

t2y.hatenablog.jp

おかげさまでまだ売れ行きは好調なようです。本書を購入していただいた方々、ありがとうございます。

このたび、第4刷の重版が決定しました。

【第4刷】3月16日(月)重版出来

book.impress.co.jp

Chapter 10 「065 Web ページを Selenium で操作する」の修正について

第4刷で本節の一部のコードが修正されます。 Selenium という Web ブラウザを操作するライブラリの使い方を紹介している節です。

これまでは Yahoo! JAPAN のトップページを題材として Selenium の操作を説明していました。

PC 版 Yahoo! JAPAN のトップページを 2019 年 10 月 1 日に刷新、 ... (中略) ... Yahoo! JAPAN トップページは 2008 年 1 月 1 日に大規模なリニューアルを行いました。その頃からある程度の改修はあったものの、基本的にはコードの継ぎ足しで修正を加えている状態でした。

techblog.yahoo.co.jp

なんと11年間変わらなかったトップページが2019年10月1日に大きな改善が入り、トップページの HTML の構造が変わってしまい、本書で説明している方法では動作しなくなってしまいました。

そこで第4刷では次のように Wikipedia のトップページに対して同じ操作を行うように変更しています。 申し訳ありませんが、第1-3刷を購入された方は次の内容に読み替えて Selenium の操作を確認してください。

変更した内容

まず、Wikipediaトップページの URLを指定してブラウザに表示させます。get()というメソッドを使います。

>>> driver.get("https://ja.wikipedia.org/")

f:id:t2y-1979:20200229144421p:plain

検索語として「Python」と入力します。検索ボックスには searchInput というid属性が付与されているのでCSSセレクターで指定して、send_keys()でキー入力を送信します。

>>> driver.find_element_by_css_selector("#searchInput").send_keys("Python")

f:id:t2y-1979:20200229144504p:plain

検索候補ウィンドウの検索ボタンにはid属性 searchButton が付与されているので、同様にCSSセレクターで指定して、click()でクリックします。

>>> driver.find_element_by_css_selector("#searchButton").click()

続けて、ブラウザの「戻る」を行ってWikipediaのトップページに戻ったあとに、"ログイン"というリンクを開いてみましょう。

>>> driver.back()
>>> driver.find_element_by_link_text("ログイン").click()

ビジネスPythonを学ぶ会の活動と slack の紹介

本書の読者サポートも兼ねて私が神戸で運営しているコミュニティがあります。 このコミュニティは難しいことを扱うわけではなく、またすぐに業務で役に立たないかもしれませんが、コミュニティとして長く続けられることだけを目標にしています。Python で困ったときに気軽に相談できる場になればいいなと思います。

bizpy.connpass.com

本書の対象読者と同様、Python 入門を終えたばかり初学者、さらに IT エンジニアではない次の方々を対象としています。

いまのところ、月1回の勉強会を開くことを予定しています。地理的に参加できない方もいるでしょうからオンラインでのコミュニケーションが取れるように bizpy.slack.com を開設しています。興味のある方はこちらの 招待リンク からご参加ください。

いまは Excel ファイルを操作する openpyxl ライブラリの操作について取り上げています。そうすると、プログラマーではないけれど、企業でデータ分析の実務を行っている方々が参加してくれます。参加者の実際の実務で行っている内容をみんなで共有したり、参加者が書いたコードをもとにレビューしたり、私が開発やデバッグに役立つ手法を紹介したりといった、そういうゆるい感じの勉強会をしています。

今後も勉強会や読者サポートの過程でお伺いした内容や気づきを留めていき、いつか本書を改訂できる機会ができたときに活かせるように努めていきたいと思います。

まとめ

本書の出版を契機にして、これまで私が接点のなかった方々と Python プログラミングを通してお話する機会が増えました。

私自身、プログラマーとして10年以上働いてきて、ちょうどいまキャリアの棚卸しをしている時期です。今後は培ってきたプログラミングのスキルを活かして世の中の役に立つプロダクトやビジネスを創造することにも挑戦していきたいと考えています。ビジネスパーソンの方々が普段行っている業務や課題意識を知る機会があることは、私にとっては新鮮で興味深いものです。

またプログラミングできる人が増えること自体もいまの世の中にとっては良いことだろうと私は考えています。

リファレンス

t2y.hatenablog.jp

できる 仕事がはかどるPython自動処理 全部入り。の第3刷が決定しました

先日、ビジネスパーソン向けの Python 本を執筆したことを書きました。

t2y.hatenablog.jp

おかげさまで書店ならびにオンライン書店でも好調な売れ行きであるそうです。本書を購入していただいた方々、ありがとうございます。

このたび、第3刷の重版が決定しました。

【第3刷】9月2日(月)重版出来

book.impress.co.jp

オンライン書店による不評から始まった読者サポート

本書は発売当初からリアル書店では好調な売れ行きであると聞いていたものの、オンライン書店では微妙な状況でした。それはおそらく INTERNET Watch での書籍紹介の記事が変なバズり方をしたせいだと私は考えています。

internet.watch.impress.co.jp

本書の対象読者は Python 入門を終えたばかり初学者、さらに IT エンジニアではない次の方々を対象としています。

書名から誤解を招きやすい点や RPA の気運が高まりつつある背景の中、INTERNET Watch の記事をみて (中身を確認せずに) IT エンジニアの方々もオンライン書店で購入された方が多かったのではないかと推測します。

Amazon のカスタマーレビューを時系列にみていただければわかりますが、当初は本書の内容にがっかりしたレビューが多かったです。著者からすると、意図した対象読者へ届いていないことを歯がゆく思ったこともありました。とはいえ、経緯がどうであっても出版してしまったものはしょうがないのでできる範囲でサポートをしようと、身近なところで読者サポートのための勉強会を隔週で開催してきました。

bizpy.connpass.com

回を重ねるごとに勉強会のメンバーも少しずつ増えてきたこともあり、「ビジネスPythonを学ぶ会 (bizpy)」の slack も準備しているところです。いまのところ、読者サポートの勉強会は関西でしか開催できません。もしオンラインでもよいので本書の内容についてやり取りしたいという方は connpass の「ビジネスPythonを学ぶ会」のグループメンバーに登録しておいてください。後日、こちらの準備が整い次第、bizpy slack への招待リンクをグループメンバーへ送る予定です。

さらに Python イベントにも登壇して本書はビジネスパーソンやノンプログラマー向けの、Python を入門を終えた方々を対象としていると訴えることにしました。そのときの登壇資料が次になります。

www.slideshare.net medium.com

どんな理由であれ、本書を手に取っていただける読者が増えることは著者にとって嬉しいことではありますが、必要のない人にまで届けようとは思いません。今後も本書はビジネスパーソンやノンプログラマー向けの書籍であることを訴求していこうと思います。

そういった活動の成果が出てきたのか、2019-07-13 時点では 2.7 だったカスタマーレビューも本日時点で 3.1 まで上がりました。ここ最近は本書の対象読者である方々によるレビューが続いているおかげだと考えています。

f:id:t2y-1979:20190812134943p:plain
2019-08-12 時点での Amazon カスタマーレビュー

またレビューの良し悪しに関わらず、レビューを投稿していただいた読者の方々には感謝を申し上げます。よいレビューは著者にとって励みになりますし、わるいレビューは気づきを得られるのでどちらも次の行動への取っ掛かりになります。

例えば、わるいレビューから得た気づきで書いた補足記事が次になります。残念ながら重版となる第3刷ではこの補足記事を追加することはできませんでした。しかし、この補足記事へのリンクを「ここもポイント」というコラム形式で「Chapter 8 テキストデータの処理」の最後に追記しました。

t2y.hatenablog.jp

今後も勉強会や読者サポートの過程でお伺いした内容や気づきを留めていき、いつか本書を改訂できる機会ができたときに活かせるように努めていきたいと思います。

重版情報をスクレイピングしてみよう

第3刷の発売が決定しましたという案内だけではおもしろくないので本書の内容でできることの宣伝も少し書いてみます。丁寧な説明は意図的に省きますのでもう少し詳しい説明を読みたくなったら本書の購入を検討してください。

重版情報はインプレスさんのスタッフブログに公開されていますが、どうもこのサイトは RSS が提供されていないようです。

book.impress.co.jp

重版情報をチェックしたい方がいるかどうかわかりませんが、こういったときは Web スクレイピングすることでほしい情報を取り出すことができます。本書の「062 Web ページから要素を取り出す」のサンプルコードを応用して挑戦してみます。また本書の「061 Web スクレイピング」の節にある注意事項も書いておきます。スクレイピングに慣れていない方は、スクレイピングを行うときのマナー、約束事項として次のことを必ず守るようにしてください。

  1. 取得したデータは個人的に参照するのみに留める
  2. スクレイピング禁止となっていないかWebサイトの規約を確認する
  3. 大量のリクエストを送らない

本稿で紹介するスクレイピングのサンプルコードは次の URL からダウンロードできます。

CSS セレクターを取得する

Google Chrome (以下Chrome) に限った話ではありませんが、最近の Web ブラウザには開発者のデバッグを助けるためのツールが標準でインストールされていてすぐに使える状態になっています。本書では Chrome を使って説明しているので同様に Chrome を使います。インプレスさんのスタッフブログを Chrome の開発者ツールを使って HTML の要素を表示させたものが次になります。

f:id:t2y-1979:20190812151227p:plain
Google Chrome の開発者ツールを使って調べる

h2 や div といった HTML タグの構造や CSS の class としてどのようなものが設定されているかがすぐにわかります。

本のタイトルと重版についての情報を書いているところが離れているのでそれらを含む上位の div.block-book-detail までの CSS セレクターを Chrome 機能で取得します。

body > div.block-wrap > div.block-content > main > div > div:nth-child(3) > div.block-book-detail-box-body.module-usr-text > div.block-book-detail

Beautiful Soup の Tag オブジェクトを使う

HTML の要素を取り出すために本書では Beautiful Soup というツールを使います。Beautiful Soup で CSS セレクターを用いると Tag オブジェクトが返されます。先ほどの CSS セレクターで取得した Tag オブジェクトが detail にセットされます。そこからさらに CSS セレクターを指定して必要な要素のみを取り出します。

    hrefs = detail.select('h2 > a')
    reprint_messages = detail.select('div.module-book-copy > b')

このとき hrefsreprint_messages は次のようなリストが返されます。

ipdb> pp hrefs
[<a href="https://book.impress.co.jp/books/1118101147" title="できる 仕事がはかどるPython自動処理 全部入り。">◆ できる 仕事がはかどるPython自動処理 全部入り。</a>,
 <a href="https://book.impress.co.jp/books/1119600001" title="ねじ子のヒミツ手技#">◆ ねじ子のヒミツ手技#</a>,
 <a href="https://book.impress.co.jp/books/1119600002" title="ねじ子とパン太郎のモニター心電図 改訂版">◆ ねじ子とパン太郎のモニター心電図 改訂版</a>]
ipdb> pp reprint_messages
[<b class="module-book-price" style=" font-size: 120%; border: 0px; ">【第3刷】92日(月)重版出来</b>,
 <b class="module-book-price" style=" font-size: 120%; border: 0px; ">【第4刷】92日(月)重版出来</b>,
 <b class="module-book-price" style=" font-size: 120%; border: 0px; ">【第2刷】92日(月)重版出来</b>]

個別に要素のリストを取得できました。これらの情報を一緒に扱いたいとき、方法はいくつかありますが、zip() という組み込み関数で組み合わせるのがシンプルに表現できます。

ipdb> pp list(zip(hrefs, reprint_messages))
[(<a href="https://book.impress.co.jp/books/1118101147" title="できる 仕事がはかどるPython自動処理 全部入り。">◆ できる 仕事がはかどるPython自動処理 全部入り。</a>,
  <b class="module-book-price" style=" font-size: 120%; border: 0px; ">【第3刷】92日(月)重版出来</b>),
 (<a href="https://book.impress.co.jp/books/1119600001" title="ねじ子のヒミツ手技#">◆ ねじ子のヒミツ手技#</a>,
  <b class="module-book-price" style=" font-size: 120%; border: 0px; ">【第4刷】92日(月)重版出来</b>),
 (<a href="https://book.impress.co.jp/books/1119600002" title="ねじ子とパン太郎のモニター心電図 改訂版">◆ ねじ子とパン太郎のモニター心電図 改訂版</a>,
  <b class="module-book-price" style=" font-size: 120%; border: 0px; ">【第2刷】92日(月)重版出来</b>)]

あとは出力するときに Tag オブジェクトから必要な属性を指定するだけです。

最終的なサンプルコードは次の通りです。

import requests
from bs4 import BeautifulSoup

page_data = requests.get('https://book.impress.co.jp/staff_blog/').text
page = BeautifulSoup(page_data, 'lxml')
details = page.select("""
body > div.block-wrap > div.block-content > main > div > div:nth-child(3) > div.block-book-detail-box-body.module-usr-text > div.block-book-detail
""")

for detail in details:
    hrefs = detail.select('h2 > a')
    reprint_messages = detail.select('div.module-book-copy > b')
    hrefs_and_reprints = zip(hrefs, reprint_messages)
    for href, reprint in hrefs_and_reprints:
        print(f'{href["title"]}: {reprint.text}')

実行結果は次の通りです。

$ python impress_book_reprint_list_all.py
できる 仕事がはかどるPython自動処理 全部入り。: 【第3刷】9月2日(月)重版出来
ねじ子のヒミツ手技#: 【第4刷】9月2日(月)重版出来
ねじ子とパン太郎のモニター心電図 改訂版: 【第2刷】9月2日(月)重版出来

私が興味があるのは「できるPy」のみなので、さらに if 文で条件を限定することでほしい情報のみを絞り込めます。

    for href, reprint in hrefs_and_reprints:
        if href["title"] == 'できる 仕事がはかどるPython自動処理 全部入り。':
            print(f'{href["title"]}: {reprint.text}')

実行結果は次のようになります。

$ python impress_book_reprint_list_dekiru.py
できる 仕事がはかどるPython自動処理 全部入り。: 【第3刷】9月2日(月)重版出来

まとめ

本書の「062 Web ページから要素を取り出す」のサンプルコードを応用して RSS 情報のないサイトのページを取り出してみました。

本書で扱う「自動化」というのはこの程度の内容を十数行から数十行のサンプルコードで解説します。このぐらいのレベル感で目次にある内容の、Python プログラミングやツール・ライブラリの使い方を知りたいという方にとっては本書は役に立つでしょう。もしくは、このぐらいの内容なら自分で調べてすぐに実装できるという方であれば本書は不要になります。

どちらとも言えない方はぜひリアル書店へ足を運び、そこで中身を少し読んでみて、その直感で選択していただければと思います。

リファレンス

少し古いバージョンですが、Beautiful Soup ドキュメントの日本語訳もありました。

kondou.com

できる 仕事がはかどるPython自動処理 全部入り。 (「できる全部入り。」シリーズ)

できる 仕事がはかどるPython自動処理 全部入り。 (「できる全部入り。」シリーズ)

Python で Unicode 正規化 NFC/NFD の文字列を扱う

先日、ビジネスパーソン向けの Python 本を執筆したことを書きました。

t2y.hatenablog.jp

本稿では本書のことを「できるPy」と呼びます。

Amazon でいくつかカスタマーレビューもいただいて次のコメントをみつけました。

python3.7 対応ということで、pathlib を使ってる点が(古いpython は切り捨てる!的なところは)潔いと言えば潔いし、日本語のファイル名にも気を配っている記述はオライリーに期待するのは酷なところもある。でもこの本でもNFD問題は全くの記述無し。だめだろ、それじゃ。

Amazon CAPTCHA

まさに仰る通りです。執筆時にそのことに気づかずご指摘いただいてありがとうございます。

ここでご指摘されている NFD 問題というのは、ファイル名のみに限った問題ではなく、Unicode文字集合を扱ってエンコード/デコードするときに発生する問題です。ASCII 文字しか扱わない開発者が書いたコンテンツとの差別化を図るという意味でも付加価値になり得るコンテンツだと私も思います。

本稿では「できるPy」に掲載できなかった NFC/NFD 問題について説明しようと思います。私の一存では決められませんが、本書を改訂できるタイミングがあれば、出版社と相談して本稿の内容も追加してもらうように働きかけようと考えています。

本稿で紹介するサンプルデータならびにサンプルコードは次の URL からダウンロードできます。

NFC/NFD 問題とは

Unicode は世界中のすべての言語で使われる文字を扱う文字集合です。Python は内部的に Unicode で文字列を扱います。例えば、UTF-8エンコードされたファイルの内容を読み込むコードが次になります。

import sys

filename = sys.argv[1]
with open(filename, encoding='utf-8') as f:
    for line in f:
        print(line.strip())

Unicode では1つの文字を表すのに複数の文字を組み合わせることができます。そして、同じ文字が別々の表現だと検索や置換などに不都合があるのでどれか1つの表現に統一することを 正規化 と呼びます。その正規化形式の名前の頭文字をとって NFC、NFD と呼びます。

  • NFC: Normalization Form Canonical Composition (合成済みの文字)
  • NFD: Normalization Form Canonical Decomposition (複数文字を結合した文字列)

言葉だけではわかりにくいので実際にその違いをサンプルデータとともに説明します。いま NFC/NFD で正規化されたテキストファイルを前述したプログラムを使って中身を表示してみます。

実行結果。

$ python3 read_file_and_print.py NFC_sample1.txt
プログラミング

$ python3 read_file_and_print.py NFD_sample1.txt
プログラミング

どちらも「プログラミング」と表示されました。OS 環境や OS バージョンによって正しく表示されない可能性もあります。私の環境 (macOS 10.14.4) では見た目上は全く同じに表示されます。

このとき変数 line には UTF-8 でデコードされて Unicode の文字列がセットされます。次に NFC/NFD 問題をイメージしやすいように Unicode の文字列を文字単位で扱うサンプルを紹介します。

import sys
import unicodedata

def show_unicode_name(line):
    for char in line.strip():
        name = unicodedata.name(char)
        space = ' '
        if unicodedata.combining(char) != 0:
            space += ' '
        print(f'{char}{space}: {name}')

filename = sys.argv[1]
with open(filename, encoding='utf-8') as f:
    for line in f:
        text = [char for char in line.strip()]
        print(f'文字単位: {text}, 長さ: {len(text)}')
        show_unicode_name(line)

実行結果。

NFC で正規化されたテキストを文字単位で分割して Unicode データベースに登録されている名前を表示する。

$ python3 read_file_and_show_unicode_name.py NFC_sample1.txt
文字単位: ['プ', 'ロ', 'グ', 'ラ', 'ミ', 'ン', 'グ'], 長さ: 7
プ : KATAKANA LETTER PU
ロ : KATAKANA LETTER RO
グ : KATAKANA LETTER GU
ラ : KATAKANA LETTER RA
ミ : KATAKANA LETTER MI
ン : KATAKANA LETTER N
グ : KATAKANA LETTER GU

NFD で正規化されたテキストを文字単位で分割して Unicode データベースに登録されている名前を表示する。

$ python3 read_file_and_show_unicode_name.py NFD_sample1.txt
文字単位: ['フ', '゚', 'ロ', 'ク', '゙', 'ラ', 'ミ', 'ン', 'ク', '゙'], 長さ: 10
フ : KATAKANA LETTER HU
 ゚  : COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
ロ : KATAKANA LETTER RO
ク : KATAKANA LETTER KU
 ゙  : COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
ラ : KATAKANA LETTER RA
ミ : KATAKANA LETTER MI
ン : KATAKANA LETTER N
ク : KATAKANA LETTER KU
 ゙  : COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK

NFD で正規化された文字列では濁音や半濁音が別の文字として扱われていて、Unicode の文字として数えたときの文字数も異なっていることがわかります。例えば「プ (U+30D7)」と、「フ (U+30D5)」と合成用半濁点 (U+309A) の組み合わせで表す「プ」の文字は Unicode 上では別の文字なのでこの違いにより、検索や置換などで一致しないということが発生します。

このような Unicode における正規化の違いによって起こる問題のことを NFC/NFD 問題と呼ばれたりします。

NFC/NFD 問題が発生する状況

OS によって Unicode の正規化形式が異なります。Windows/Linux では NFC を採用しており、昔の macOS (Mac OS X) では NFD を採用していました。同じプラットフォーム上で日本語ファイル名や日本語のテキストを扱っている分には、この問題に遭遇することは少ないかもしれません。

また macOS では NFD を採用しているという記事をみかけますが、最近の macOSiOS では NFC を使うように変わっているそうです。ファイルシステムが HFS+ から Apple File System (APFS) に置き換えられたときに変わったのかもしれません。私はその背景に明るくないため、詳しい方がいましたら教えてください。

私の環境は macOS 10.14.4 ですが、APFS を使っているせいか、NFCUnicode 正規化が行われます。

$ mount
/dev/disk1s1 on / (apfs, local, journaled)

この NFC/NFD 問題は昔の macOS (Mac OS X) で作られた日本語ファイル名や、Unicode文字集合に用いる符号化方式でエンコードされたコンテンツを Windows/Linux 環境で扱うとき、もしくはその逆のときに発生します。一方で NFD を採用していたときの macOS (Mac OS X) であっても、例えば Web ブラウザからコピペしたコンテンツは NFC で扱われたりして、同じプラットフォーム上でも NFD と NFC が混在してしまうといったことも発生するそうです。そして、見た目上は全く同じ文字にみえるため、この問題に気付くのが難しいというのが NFC/NFD 問題の厄介なところです。

Python で NFD を NFC に正規化する

さて本題です。ここまでで NFC/NFD 問題の概要を説明しました。

Python では標準ライブラリで Unicode データベースを扱うライブラリ unicodedata が提供されています。このライブラリを使うことで Unicode 文字の情報を取得したり、読み込んだ文字列に対して NFC/NFD といった正規化形式の変換をやり直すこともできます。

docs.python.org

unicodedata.normalize() の引数に NFC や NFD といった正規化形式と Unicode 文字列を渡すと変換できます。

unicodedata.normalize('NFC', unistr)

また結合文字かどうかは unicodedata.combining() に文字を渡してゼロ以外が返ってくるかどうかで判断できます。

unicodedata.combining(chr)

次のサンプルコードでは、濁音、半濁音などの結合文字があるかどうかを調べて、結合文字がある場合は NFC に変換します。

import sys
import unicodedata

def is_nfd(line):
    for char in line.strip():
        if unicodedata.combining(char) != 0:
            return True
    return False

def show_unicode_name(line):
    for char in line.strip():
        name = unicodedata.name(char)
        space = ' '
        if unicodedata.combining(char) != 0:
            space += ' '
        print(f'{char}{space}: {name}')

filename = sys.argv[1]
with open(filename, encoding='utf-8') as f:
    for line in f:
        text = [char for char in line.strip()]
        print(f'文字単位: {text}, 長さ: {len(text)}')
        show_unicode_name(line)

        if is_nfd(line):
            print('NFD から NFC への変換')
            converted = unicodedata.normalize('NFC', line)
            show_unicode_name(converted)

実行結果。

$ python3 read_file_and_normalize.py NFD_sample1.txt
文字単位: ['フ', '゚', 'ロ', 'ク', '゙', 'ラ', 'ミ', 'ン', 'ク', '゙'], 長さ: 10
フ : KATAKANA LETTER HU
 ゚  : COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
ロ : KATAKANA LETTER RO
ク : KATAKANA LETTER KU
 ゙  : COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
ラ : KATAKANA LETTER RA
ミ : KATAKANA LETTER MI
ン : KATAKANA LETTER N
ク : KATAKANA LETTER KU
 ゙  : COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
NFD から NFC への変換
プ : KATAKANA LETTER PU
ロ : KATAKANA LETTER RO
グ : KATAKANA LETTER GU
ラ : KATAKANA LETTER RA
ミ : KATAKANA LETTER MI
ン : KATAKANA LETTER N
グ : KATAKANA LETTER GU

まとめ

PythonUnicode の文字列が NFC/NFD のどちらの形式で正規化されているか、また別の正規化形式に変換する方法を紹介しました。

最近の macOSNFC を採用していることからいまが過渡期で NFC/NFD 問題は徐々に NFC に統一されていって将来的にはこういった問題が起こらなくなるのかもしれません。一方で NFD という表現形式も Unicode で定義されている仕組みであり、この仕組み自体がなくなるわけではありません。

本稿では濁音・半濁音のみを紹介しましたが、特殊な記号を複数の記号の組み合わせで表現することもできます。Unicode にはこのような表現形式があるというのを覚えておくといつか役に立つことがあるかもしれません。

リファレンス

ja.wikipedia.org

en.wikipedia.org

www.slideshare.net

gihyo.jp

qiita.com