蛇ノ目の記

技術のことも。そうでないことも。

Python入門者向けハンズオンでTAしてきた

Python入門者向けハンズオンでTAをしてきた。 普段は教えてもらうことばかりなので新鮮な体験だった。

python-nyumon.connpass.com

教える立場というと、前職で新入社員のJava研修のTAを一日だけやったことがあった程度だったので割と心配だった。Python基礎とWebスクレイピングをやるということなので、予習としてスクレイピングするやつを書いてみたりもした。

github.com

Fetch_M3_2017Fall_Circle_list · GitHub

参加者が自由にコードを書く時間は1時間程度ということもあり、そこまで難しいことは聞かれなかった。質問の内容としてはBeautiulSoup4の使い方とHTMLの構造(classとか)の割合が半々くらいだったように思う。HTMLの話はそんなにしないから、初めての人はたしかに迷うところだよね。

あと、講義パートでは教えてないzip関数を教えたりもした。

参加者のもくもく結果の中では、Weblioに載ってる英単語の意味を教えてくれるツールが面白かった。<td class="content-explanation"> の部分を取ってきてる(ここの部分は自分が聞かれたところなので覚えてる)。WeblioのURLはhttp://ejje.weblio.jp/content/英単語 なので、この構造に目をつけたのだと思う。ナイスな着目点。

#PyNyumonにも弱冠小学1年生の参加者がいた。Progateで幾つかの言語に触れているらしい(PyQもぜひ!)

PyCon JP 2017に凄い小学生がいたり、BPStudyでScratchでマイクラを作る小学生の話を聞いたりして子どもの凄さに驚いていた今日このごろだ。なんなの最近の小学生。すごい。

個人的な学びとしては、リポジトリをforkしてからPRを出すやり方を覚えた(TAの傍ら、#PyNyumonテキストに直した方がよさそうなところを見つけてた)。

そんな感じでTAをやる実績を解除した。今後は、行ってみたい地方でPyCampのTAをやってみるのもいいかもしれない

BPStudy#122に行ってきた - プログラミング教育について

10/20(金)に開催されたBPStudy #122に行ってきた。

bpstudy.connpass.com

今回のテーマはプログラミング教育について。二部構成でテーマはそれぞれ、子どもたちへのプログラミング教育、大人が対象のプログラミング教育だった。

第二部では評判が絶賛右肩上がりのPyQの開発チームも登壇した。PyQはPythonの基礎からWeb開発だけでなく機械学習にも対応、さらには初心者が見落としがちなPythonらしい書き方も学べるカリキュラムが登場している実際すごいオンライン学習サービスだ(ダイマ)。

CoderDojo柏代表の宮島さんの話に特に興味を惹かれた。現在、柏市では市内42校の全公立小学校でプログラミング教育が始まっており、CoderDojo柏はカリキュラム策定や教員への研修などをサポートしているとのことだった。技術を楽しむというCoderDojoの文化の入ったプログラミング教育は成功するだろう。2020年からの新しい学習指導要領では社会に開かれた教育というあり方が盛り込まれるそうだ。プログラミング言語の強みであるオープンソースとの相性が良さそうに感じる。市内に4つもCoderDojoの拠点がある柏市は稀有な例なのかもしれないが、それぞれの地域にあるエンジニアコミュニティと連携するなどして、開かれたプログラミング教育が始まればいいなと思う。ハードもソフトもメーカー系大手SIerの手が入って、開かれてないプログラミング教育になったりしたら割と悲しい。

それにしてもScratchでマイクラを作る小学生の話には驚かされた。Scratchという教育用言語で3DCGのゲームを作るなんて発想は大人にはなかなかできない。PyCon JP 2016で話題になったPython 2.7をdisる小学生といい、知の高速道路を突っ走っている感がすごい。2.7をdisった彼はPyCon JP 2017 ユースコーダーワークショップで講師の想定以上の開発力を発揮したなんていう噂を耳にした。来年あたりプロポーザル出してきても不思議じゃない。

Python入門者向けハンズオン #6に向けて、という体でスクレイピングの練習をしてみた

BeautifulSoupを使って海外の歌詞サイトMetroLyricsから歌詞をスクレイピングするスクリプトを書いた。10月28日(土)に開催されるPython入門者向けハンズオン #6(#PyNyumon)でメンターをやることになっているので、その予習。

github.com

こういう感じのスクリプト(READMEと同じ内容)

  • MetroLyricsからランダムに歌詞を抽出して表示する.

  • アーティスト名を引数で指定

  • 指定されたアーティストのMetroLyricsにある全曲を対象にする

  • 日本語歌詞にも対応

  • Usage

    • $ python fetch_lyrics.py "Artist Name"
  • example

    $ python fetch_lyrics.py Oasis

    $ python fetch_lyrics.py Arctic Monkeys

    $ python fetch_lyrics.py Aimer

作ってみて気づいたことなんかを書いていきたい。

  • リダイレクトしているかどうかはrequestsでは以下の方法で判断できる
>>> url = URL
>>> r = requests.get(url)
>>> r.history

MetroLyricsでは歌詞リストのURLは http://www.metrolyrics.com/{アーティスト名}-alpage-{ページ番号}.html のフォーマットになっている。登録されている曲数に応じてページ数が変わるわけで、それ以上のページ番号を指定すると404が返ってくるという予想の元で進めていた。しかし実際はhttp://www.metrolyrics.com/{アーティスト名}-lyrics.htmlにリダイレクトする。リダイレクトしている場合、上記r.historyの戻り値は[<Response [302]>]になる。リダイレクトしていない場合は空のリストが返ってくる。

  • スクレイピングした結果が文字化けした場合は、BeautifulSoup側でデコードさせる

海外のサイトということで取得する歌詞はすべて英語であるという前提だったが、試しにAimerEGOISTと入れてみた結果、歌詞リストが存在しており歌詞が取得できた。しかし日本語歌詞は文字化けを起こしていた。

requestsオブジェクトをrとしたとき、r.textとすると文字化けが発生しやすい。このr.textではデコード済みのレスポンスボディをstr型で返している。r.contentbytes型のレスポンスボディを取得してBeautifulSoupに渡すことでBeautifulSoup側でデコードしてくれる。

スクレイピングでの文字化けを防ぐ方法の詳細は以下のブログが詳しい。Pythonクローリング&スクレイピングの著者による解説なので心強い。

orangain.hatenablog.com

#PyNyumonでは特に後者で躓く受講者がいるかもしれない。そのときに解決の手助けができたら、いいなぁと思う。

実は今回の試みは、spaCyに食わせる英語テキストが欲しくて始めた。そのspaCyのチュートリアルはしばらく放置してしまっている…。

再び、劇場版 響け!ユーフォニアムを観てきた

劇場版 響け!ユーフォニアム 届けたいメロディ新宿ピカデリーで観てきました。

2回連続で技術と関係ない話というのは如何なものか。 申し訳程度にそれらしい話をすると、観た回が22時05分という遅い時間帯だったため、それまでファミレスでDjango Girls Tutorialをやっていた。ディレクトリ構成を会社で覚えた構成にするなどアレンジして進めている。

2週続けて観た理由は今週のフォトセッションが田中あすか先輩達3年生組だから。これに尽きる。

f:id:Nao_Y:20171020004737j:plain

香織先輩もかわいい。ほくろがポイント高い。

せっかく2週続けて観たのだから、もう少し真面目に書きたい。前回、今作は久美子とあすか先輩の物語だと書いた。そこでは久美子の姉、麻美子の話を書き漏らしていた。これについてもやはり物語る亀さんが触れているのだが、あえて自分の言葉でまとめたい。

久美子がユーフォニアムを始めたきっかけは吹奏楽をやっていた姉、麻美子への憧れにある。しかし麻美子は高校に入ると、大学受験を理由に吹奏楽から離れてしまう。今作では、好きなことを続けることができなかった自分への後悔を久美子に打ち明ける。「後悔するような選択はしないでほしい」。これはその後、姉と同じ理由で全国大会の舞台から去ろうとしていたあすか先輩に、久美子が向ける言葉でもある。つまり、久美子にとってあすか先輩は、"一緒に演奏することができた"というあり得たかもしれない姉の可能性でもある。だから、久美子は他の誰よりも、あすか先輩と演奏したい。

久美子は姉が家を出ていった後、自然と涙するほどに実は姉を慕っていた。そしてあすか先輩にはあり得たかもしれない姉の可能性を見出すことができる。一方、あすか先輩は久美子を"ユーフォニアムのような子"と表現する。言うまでもなくあすか先輩はユーフォニアムへの想いはあまりに深い。互いを自分の大切な存在と重ねる二人の関係を百合と言うほか何があるのか。「こんなにユーフォニアムらしい子がいるなんて」あすか先輩が久美子にそう伝える川辺のシーンは実質、告白だ。さらに「自分はユーフォニアムらしくないと思っている」と、自分に無いものを相手に見出す。これが百合ではなかったら何が百合なのか。某ライムスター宇多丸めいた表現をするなら、この百合感は文句なく5億点

渡り廊下で、全国大会の舞台に引き留めようとする久美子に冷たい言葉を向けたあすか先輩の内側を今も考えている。諦めることを自分に受け入れさせるために相手が傷つくとわかっていることを言う、そんな自分勝手さに彼女自身も傷ついているかもしれない。これまでもそうだったのではないか、とかそんな感じに。田中あすか好きの行き過ぎた思い入れだ…。

前回買ったパンフレットから、駅ビルコンサートの絵コンテは山田尚子であるという情報を得たため気合を入れて観ていた。いきなり細かい話をするが、麗奈の耳にかけた髪が耳から落ちる、というカットがある。観ていてここには心底はっとした。体の動きだけでは伝わらない躍動感みたいなものかもしれない(高坂麗奈と誕生日が同じで、謎に好感度が高いというのもある)。

そして駅ビルコンサートの見どころは晴香部長のソロだと思っている。部長でありながら普段はそこまで目立たない彼女のサックスソロ、その頑張りが本当に表情に出ている。吹奏楽の楽器としてはサックスとトランペットが好きなので、それもあるかもしれない。

と結局、今回も山田尚子推しをしたところで締めとする。

劇場版 響け!ユーフォニアムを観てきた

技術のことも。そうでないことも。

と謳っているのでアニメの話をしても実際問題ない。この記事の更新が会社のSlackの#tech-blogsに流れてしまう方が気まずい。

「劇場版 響け!ユーフォニアム 届けたいメロディ」を観てきた。 9月30日から公開されており、今週が公開2週目。 TVシリーズは1期、2期ともに視聴済み。劇場版前作(1期の総集編)も観ている。それぞれ圧倒的な映像と音楽のクオリティで、呼吸をしていたか曖昧になるくらい見入っていた。

本編開始前にフォトセッションなる短編があり、ここでは写真撮影可のカットが流れる。カットは毎週変わるあたり商売上手だ。今週は中川 夏紀たち2年生4人だった。

劇場版だが実際のところは総集編。とは言い条、TVシリーズ13話を2時間の尺に無理に詰め込むのではなく、シナリオの筋を久美子とあすか先輩の関係に絞るという大胆なアレンジを行っている。そのためにTVシリーズで描かれた2年生同士の確執や滝先生の過去話はカットされているし、合宿の時間軸もずらされている。

TVシリーズ序盤で話題になった久美子と麗奈の引力は残念ながらフォーカスされない。しかし百合成分は損なわれていないので、ごあんしんだ。むしろ今作は久美子とあすか先輩の関係が引力以上に熱い。

メインを久美子とあすか先輩に絞ったことでユーフォニアムに触れるきっかけになった人に演奏を聴かせたいという二人の共通した全国大会へのモチベーションが際立ったように思う。今作は久美子とあすか先輩、二人の物語へと姿を変えている。

響け!といえば映像と音楽のクオリティというくらい質が高い。音楽に関してはあまり語れないので、映像の話をしたい。会社での自己紹介で山田尚子作品が好きというくらいに好きなので、彼女がシリーズ演出を手掛ける本シリーズでも、山田尚子っぽいところを観ては喜んでいた。

過去の山田尚子監督作品(けいおん劇場版やたまこまーけっとたまこラブストーリー)に共通する、浅い被写界深度と柔らかい光は本作でも健在だ。被写界深度といえば、奥行きのある画で手前の人物が喋りだした途端にそちらにすぅっとフォーカスが移る演出が特に気に入っている。

とにかく彼女の作る画は、青春という雰囲気がする。

もう一つ、山田尚子といえば脚の演技が特徴的(けいおん劇場版終盤など)で、これに関しては物語る亀で詳細に言及されている。ほとんどの登場人物が白ソックスであるのに対して、何を思っているのか見透かせないあすか先輩だけが黒のタイツというのを上手く使って、あすか先輩が久美子と本音で話すシーンでだけ素足というのはとても納得がいく。2回目を観るときは脚の演技にも注目したい。

響け!の登場人物では田中あすかが一番好きなので、出番の多い今作は彼女のファンとしても満足だった。という締め。2回目を観た後にでも、また何か書きたい。

flake8をGit pre-commit hookした

flake8でチェックするのを忘れがちなので、Git pre-commit hookした。Docs » Using Flake8 » Using Version Control Hooksにやり方が書いてあるんだけど、日本語での検索結果が少ないのでせっかくなので記事にする。

Gitで管理してるプロジェクトのルートで $ flake8 --install-hook git

これだけでコミット時にflake8が走るようになる。

ただ、チェックに引っ掛かったファイルはコミットできないようにしたいので、以下のように設定を変える。

$ git config --bool flake8.strict true

これでOK。

会社のPCでは後半の設定をまだやってないので、出社したら済ませておこう。

assertRaises()で嵌った話

今、テストコードを書いている。前職ではテストコードを書く文化が無かったのでこれが初めて。ユニットテストフレームワークunittestを使っている。

これは初めてユニットテストを書いた男が数時間同じ箇所で嵌り続けたという話だ。格好悪い。

まず、assertRaisesとは何か。公式ドキュメントには、

assertRaises(exception, callable, *args, **kwds)

callable を呼び出した時に例外が発生することをテストします。

とある。これを読んですぐにテストを書き始めた。要するにexceptionにテストで検出したい例外、callableにテスト対象の関数を書けばいいんだろ、というぐらいの勢いで。じゃあ*args **kwargsは何のためにあるんだよ、と今になって思う。

ここからは簡単のため、単純に除算をするだけの関数(division.py)を例としていく。

def division(dividend, divisor):
    try:
        answer = dividend // divisor
    except ZeroDivisionError:
        return None
    return answer

という関数をテストしたい。除算の結果を返すだけだけど、ZeroDivisionErrorではNoneを返す。というわけでテストを書いた。余談だけど_call_fut()でテスト対象関数を呼び出す方法は@aodagさんの効果的なunittest - または、callFUTの秘密で学んだ。

import unittest

class TestDivision(unittest.TestCase):

    def _call_fut(self, *args):
        from division import division
        return division(*args)

    def test_valid_answer(self):
        actual = 5
        self.assertEqual(actual, self._call_fut(25, 5))

    def test_zero_division_error(self):
        self.assertRaises(ZeroDivisionError, self._call_fut(25, 0))

では、こいつを実行するとどうなるか。

assertRaises_practice/tests/test_division.py:16: DeprecationWarning: callable is None
self.assertRaises(ZeroDivisionError, self._call_fut(25, 0))

こうなる。callableがNoneだぞと言っている。このcallableがわからずに2時間くらい嵌り続けた。テスト対象関数の戻り値をNone以外にもしたし、raiseさせてみたりもした。関数の戻り値をraiseにすると、

ZeroDivisionError: division by zero

こうなる。ZeroDivisionErrorが発生することを確認するテストがZeroDivisionErrorで落ちる。これはひどい

ここらの足掻きをひたすら書くのも冗長に過ぎるので、先輩方に助けによって得た学びを以て、一足飛びに結論に行く。

実は先のZeroDivisionErrorになってしまう件によって、どこがおかしいか気付きやすくなった。例外で落ちるということはassertRaises()の中で呼び出されて欲しいdivision(25, 0)が先に実行されているということになる。そしてassertRaises()が失敗するということはこれはcallableではない。オブジェクトが呼び出し可能オブジェクト (=callable)であるか判定してくれる組み込み関数callableで確認してみる。

>>> def division(divided, divisor):
...     try:
...             answer = divided/divisor
...     except ZeroDivisionError:
...             raise
...     return answer
>>> 
>>> callable(division(25,1))
False

(エラーが出てしまうため引数は変えている)

division(25,1)callableではないことがわかった。公式ドキュメントを見てみよう。

なお、クラスは呼び出し可能 (クラスを呼び出すと新しいインスタンスを返します) です。また、インスタンスはクラスが __call__() メソッドを持つなら呼び出し可能です。

とある。つまりクラスではないdivision(25, 1) = float型のオブジェクト__call__()メソッドを持っていないということだろうか。float型のインスタンスであること、__call__()を持たないことを 確認してみる。

>>> answer = division(25, 1)
>>> isinstance(answer, float)
True
>>> '__call__' in dir(answer)
False

これで間違いがはっきりした。assertRaises()には、テストしたい関数を引数を与えた状態で入れてはいけない。

ではどうすればいいのか。俺はどうしてもdivision()をテストしたいんだ。ならば引数を渡さない形にするとどうだろう。

>>> callable(division)
True

ようやくたどり着いた。callableには関数を入れるべきだった。正しいアサーションはこうなる。

self.assertRaises(ZeroDivisionError, self._call_fut, 25, 0)

ここまで来るのに随分掛かった……。

他のアサーションではself.assertEqual(1, self._call_fut(5, 5))のようにメソッドの中に戻り値を入れるけど、assertRaises()の引数callableは先に実行されるとテストメソッドが例外で落ちてしまう。つまり、assertRaises()が実行されてから、引数callableを実行してほしいからこうなるということ?

最後に念の為、divisionの有効な属性を確認しておく。

>>> dir(division)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

__call____class__があるけど、クラスとして呼び出し可能なのか、__call__()メソッドを持つインスタンスとして呼び出し可能なのか……。謎が残ってしまった。