川崎2-0湘南 (圧勝:ホーム初勝利)

金曜夜の等々力。今年はなかなか勝ち試合が見れないままここまで来ている。あの齋藤学のゴールでギリ勝ったACL以来か。しかし、クソ強かった時と何が違うんだろうね。怪我人とか、あと右サイドは当然違うが…

まあこの試合は先制点の時間も良くて、割と安定して試合を進められたんじゃないかな。そろそろ小林悠にも1本欲しいところ。そのへんは時間の問題ではある。外すのも決めるのも、波があるので。ビッグウェーブを待ってる感じで見ております。

Pythonのclickで共通オプション

またもやPythonねたです。前回言っていたimportのフォーマットは、dirimportという形で公開してみました。pip install dirimport …使ってみてもらえればと。今後の自分のrequirements-dev.txtの常連になるか?

今回はclickについて。我々の日常はPythonでclickを使ってサブコマンドを作っていく毎日だが、、、そこにはパターンというものがある。

まずはサブコマンドの定型句がある。君たちはこれを何度書かせるつもりかね?

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    if ctx.invoked_subcommand is None:
        print(ctx.get_help())

で、こんな感じでサブコマンドを書いていく。

@cli.command()
@click.option("--verbose/--no-verbose")
def subcmd(verbose):
    pass

ここで、サブコマンド間の共通のオプションっていうものが考えられる。これは毎回いくつもの共通オプションを書くのはめんどくさいので、1つのデコレータにマージするテクニックがある。デコレータ…毎回ググらないと書ける気がしないやつね。

_common_options = [
  click.option("--verbose/--no-verbose"),
  click.option("--arg1"),
  click.option("--arg2"),
]

def common_option(func):
    for option in reversed(_common_options):
        func = option(func)
    return func


@cli.command()
@common_option
@click.option("--other-option")
def subcmd(verbose, arg1, arg2, other_option):
   pass

しかし全部のサブコマンドに共通ってわけじゃなくて、複数の共通セットがあってサブコマンドの種類ごとに変わります、みたいなことになると、、、これで個別にデコレータを増やしていくのもきつくなる。どうするか…

そこで、引数のオプション配列を処理してくれるデコレータにする、というテクニックが出てくる。

def multi_option(decs):
    def deco(f):
        for dec in reversed(decs):
            f = dec(f)
        return f
    return deco


@cli.command()
@multi_option(_common_options)
@click.option("--other-option")
def subcmd(**kwargs):
   pass

実際はmulti_optionsからcommon_optionを作ることになるんだろう。

def common_option1(func):
    return multi_option(_common_options)(func)

@cli.command()
@common_option1
@click.option("--other-option")
def subcmd(**kwargs):
    pass

ここまでやるとclickにこだわるんじゃなくて、argparseでいいような気もするね。

def common_parser(parser=None):
    if parser is None:
        parser = argparse.ArgumentParser()
    parser.add_argument(...)
    parser.add_argument(...)
    return parser

def main():
    p = common_parser()
    p.add_argument("--other-option")
    opt = p.parse_args()
     :

もうちょっと頑張って何か作ったら、うまいこと省力化できそうな気もしますよね。どうしようかな。

川崎1-1セレッソ (連敗脱出)

セレッソ戦で負けなかったのっていつ以来なんだろうね。

ポカポカ陽気で花見日和だったんだけど、強風。後半になるとちょっと寒くなったかな。

試合の方は、前半は両者ともチームカラーを押し出して試合を進めていくが、お互いになかなかシュートに持ち込めない。そのまま前半が終わった感じだったけど、セレッソの方はワンチャンスにちゃっかり1点取ってきた。唯一のチャンスを決めた柿谷を褒めるしかないでしょうな。

後半は開始から押し込んでゴール前で崩したと思ったら若干混戦っぽくなりつつ知念が決めて同点に。小林悠や長谷川を入れて前線を活性化させ、勝ちをもぎ取りに行くもゴールは遠く、試合は終わった。小林悠はキレが戻ってきている感じ。ボールを預けるたびに、ざくざくチャンスを作り出す。あの感じね。あとはフィニッシュか。

ボクシングの試合の宣伝もあった。改元後最初の国内世界戦で、黒田選手がチャンピオンに挑むらしい。後楽園ホール…いいねえ。応募しようかな。

最後はセレッソサポが山村コールをしていた。会場に来てたら挨拶に出て来るんだろうな…山村がセレッソにいたら2点目取られてたかもな…などと思いながら暗い道を歩き、そのまま自宅に帰った。

川崎91-67三遠 (プレーオフ決定)

久しぶりにとどろきアリーナへ。ずいぶん久しぶりだなぁ。リポビタンDを振る舞われて浮かれ気分になりつつ、試合を待つ。しばらくぶりということもあって、いろんなものの配置が変わっていたことに戸惑う。一家団結。

会場の雰囲気は良かったです。ワールドカップ本戦出場を決め、オリンピックの開催国枠も認めてもらえて、代表の活躍が競技自体の人気を引っ張ってくれているという面もある。セックス(アリーナDJ)の選曲は相変わらずで。まあ、ああいうのも悪くない。

外はかなり寒かったので、室内競技はその点快適。ポカポカなら桜でも見たいところだったという話もあるんだけれども。しかし金曜はフロンターレに行くとすると外だから、この寒さは厳しいなぁ。金曜は同時刻にバスケもあるから中を選択するという手もある…

今日の試合自体は、1Qのシュート成功率が高く、ディフェンスもしっかり行けたことにより点差をつけたのがでかかった。それにより控えの選手もリラックスして試合に入ることができ、活躍できた。鎌田とか谷口ね。最終的には全員得点。余裕を持って試合を締めることができた。まあそれでも3Qのグダグダは気になったが…

最後は篠山による家族の秘密のカミングアウトを聞きつつ、リポビタンDマンも満足、私も満足して会場を後にした。

Pythonを使った開発について

最近はPythonを使うことが多くなった。一時期はGolangという感じだったが、やはりPythonは偉い。仕事でもJavaを追放してPythonに絞っていこうとしている。このブログでもPythonネタをたまにぶち込むと思いますがよろしくお願いします。

Pythonで何かを開発するときに、まあベストプラクティス的なものがいくつかあるんだけど、そのうちの「ディレクトリ構造」に関して、自分としてはこれがいいんじゃないかな、というのが掴めてきたので、紹介します。世の中で言われているものとはちょっと異なりますので、その点はご了承ください。

まず、リポジトリのトップディレクトリに.envrcというファイルを置いて、中身は1行、[ -f bin/activate ] && source bin/activate と記します。direnvとvenvを使うというわけですね。この1行だけで、見る人が見れば分かるしdirenvを使ってる人は必ず気づく。

.gitignoreにはbin, lib, share, __pycache__, pyvenv.cfg などを入れていきます。

次に、requirements.txtを書きます。依存ライブラリの記述は近年はpipenvなんかが優勢な状況ですが、pipenvはアプリの実行にはいいけどライブラリの開発には向いていないので、使いません。作ってみて出来が良ければパッケージをPyPIに登録していくことになるんですけど、そのときの依存ライブラリの記述に使いたい、というのが主目的なんで。requirements.txtならアプリ実行/ライブラリ開発のどちらにも対応できますし、Pythonの標準機能のpipから自然に使えます。requirements.txtにはユーザが使うときに必須となる依存ライブラリを書いていき、自分が使うtwineやwheelやjupyter、coverageなどはrequirements-dev.txtみたいに別ファイルに書きます。

requirements.txtの中身には、基本はパッケージ名のみを書きバージョン指定をしません。開発中は最新バージョンに追随するので問題ないし、リリース後に問題になったらそのとき書けばいい。バージョンの書き方はpkgname<Nで、Nは最初に問題になったバージョン。問題のバージョンの1個前が入るようにします。pip freezeとかは信用しない。requirements.txtの行数はなるべく縮める。依存ライブラリの依存ライブラリとかは勝手に入るので、書かない。pipenvはこの辺の処理は便利なんですけどね。

venvもpipもPythonの標準機能に入っており追加ソフトウェアのインストールが不要というのが大きいです。ユーザはシステムワイドにインストールするかもしれないし、venvやvirtualenvやpipenvを使うかもしれないし、dockerイメージに入れるかもしれない。開発者はインストール方法を強制できないけど、Pythonの標準機能だけは当てになる。ゆえに開発者はできるだけ標準機能を使い、ユーザは自分の使いたいものを使う。

Linuxのディストリビューションとかだとpipは処理系とは別パッケージなのでpipは標準機能ではない! と思っている人もいますが、Python3系のどこか以降からvenvが標準機能として含まれるようになり、システムにpipを入れてなくてもvenvで作ったディレクトリにはpipが入ります。なのでpipも標準機能です。venvを無効にしている? そんな奴は犬の餌にでもしとけ。

パッケージはeggやtar.gzやrpmではなく、wheel形式で作ります。pip install wheelして、python setup.py bdist_wheelする。

この、パッケージングで使うファイルにsetup.pyとsetup.cfgの2つがあります。これはどちらが優れているかはまだ決めかねています。世間的にはsetup.cfgを使って、setup.pyにはsetup()のみを書く、というのが良いとされているようですが、結局setup.cfgを書いてもsetup.pyが必要になるのがなんだかなぁ…と。python -m setuptoolsみたいなコマンドでsetup()を呼んでくれればsetup.pyを捨ててこの話題は終わり、になるんでしょうけど、まだそうなってないんです。setup.pyをファイルごと捨てられる状態になるまでは、setup.cfgを使わなくてもいいかな、と思ってます。setup()の引数は、こんな↓感じ。

  • install_requires→open(“requirements.txt”).readlines()
  • extra_require[“development”]→open(“requirements-dev.txt”).readlines()
  • long_description→open(“README.md”).read()
  • long_description_content_type→”text/markdown”
  • version→(モジュール名).version.VERSION (importしておく)
  • その他は適当に。setup()呼び出し以外の処理は書かない

README.mdはmarkdown(gfm)で書きます。このREADMEは拙さ丸出しの英語で書き、使い方のサンプルを中心に書いていきます。打つコマンドと結果を羅列していくだけでなんとなく使い方が伝わる、というのが理想。

実際、OSSのドキュメンテーションってのはあんまりうまく文章を書こうとしないのがキモだと思ってます。うまく文章を書けてしまうことの弊害ってのがあって、文章の説明だけで押し切れてしまうようになり、肝心のソースがおろそかになってしまうんですね。ややこしい注意点の文章を長々と書かず、ややこしい説明なしで使えるソースにしておく、ということです。私は幸いにもややこしいことを英語で説明できませんから、英語がちょうどいい。英語が得意な人は英語を避けてドイツ語かロシア語あたりで書くようにすればいいと思います。文章でサポートできなくなるため、必然的にコードが使いやすくなります。

サンプルのデータやコード、説明のための.ipynb等はexamplesなどの名前のディレクトリを掘って、そこに置きます。jupyterの.ipynbは作るのも簡単だし、githubでプレビューできるので流通させるのにも便利なフォーマットです。

READMEのCLIの例ではvenvを使うようにします。こんな感じ↓

  • git clone https….
  • cd xxx
  • python -m venv .
  • ./bin/pip install -r requirements.txt
  • ./bin/python -m xxx.cli

ソースの配置ですが、パッケージ名でディレクトリを作り、__init__.pyを配置します。サブパッケージを作るときも毎回__init__.pyを作ります。__init__.pyの中身は決まっていて、

  • 同一階層のソースファイルfilename.pyに関して、from .filename import *
  • パッケージディレクトリdirnameに関して、from . import dirname
  • 必要なら__all__=[“シンボル名”,…]
    • これはなくてもいい。実際は__all__を書いてもシンボルを隠せるわけではなく、気休めにしかならない

このようにすることで、ユーザは import xxxだけでサブパッケージも含めて全ての機能を使えるようになります。__init__.pyをちゃんと書かないとimport xxx.filenameみたいにしないと読み込まれなかったりして、使いにくいんですね。

逆に、__init__.pyを上記のように書くことにより、ファイル名の変更の影響が少なくなるという効果もあります。ファイルの分割やマージは日常的にやりますが、ユーザに影響を与えずにそれができる。Golangみたいな感じになる、と言えばわかりやすいかな??

__init__.pyはいつか自動生成か何かで対応したいと思っていたりします。

ユニットテストはなるべく多くの部分にdoctestを使います。coverageを使ってカバレッジを見つつ、doctestでカバーできないテストはtestsディレクトリを掘って書いていく。実行はnoseを使い、nosetest -v –with-doctestで。まあでも私はユニットテストはかなりサボることが多いです。

CIは、会社とかでは社内にCIのサービスがあるので設定してREADMEにバッジをつけたりパッケージのファイルやカバレッジレポートを生成してgh-pagesに上げたりしてますが、趣味のコードではやらないです。

ログは、各ソースファイルの先頭付近に定型句を書く。

  • from logging import getLogger
  • log = getLogger(__name__)

以前はクラスの中でgetLogger()してself.log.debug()みたいな呼び出し方をしていたが、めんどくさい割に利益がなかった。今はロガーはクラスの外に置いている。ログの設定はmain()の中で、あるところまではbasicConfig(level=DEBUG)で、ちゃんとしたものになっていったらYAMLか何かで設定ファイルを作って、dictConfig()で設定する。こんなイメージ↓

  • logging.dictConfig(yaml.load(“your-config-file.yaml”).get(“log”, {}))

ソースの中で自分のパッケージの別ファイルを読み込むときは、相対importを使います。from ..pkgname import xxx みたいな。

CLIは今はclickというモジュールを使うことがほとんど。標準のargparseを使うことはほぼなくなりました。click, requests, PyYAML, jupyter。この辺はなかなか手放せない。

まとめると、パッケージxxxのリポジトリのディレクトリ構造は、、、

  • .envrc ([ -f bin/activate ] && source bin/activate)
  • .gitignore (自分用に適当に書く)
  • README.md (超カタコト英語、ほぼ実行例のみ)
  • setup.py (setup.cfgをつけるかどうかは、そのときの気分による)
  • requirements.txt (バージョン指定は必要になってから)
  • requirements-dev.txt (wheel, twine, nose, jupyterなど)
  • xxx/
    • __init__.py (from .file1 import *; from .file2 import *; from . import yyy)
    • file1.py
    • file2.py
    • yyy/
      • __init__.py (from .file3 import *)
      • file3.py
  • examples/ (実行例やサンプルデータなど雑多に)
  • tests/ (doctestに書けないユニットテスト)

ライセンスは昔はGPL一択だったけど、最近はMITにすることが多いかな。あんまり深く考えてない。LICENSEファイルをつけるとgithubが検出して正しく表示してくれます。setup()の引数にもライセンスの項目がありますね。

ふくらはぎの話

近年の陸上競技において大きな話題となった、カカトをつかない走り方。長距離では厚底シューズと道具の名前が先行したアレです。マーケティング! ただ少し考えるとわかりますが、この走法は道具の問題ではない。

道具はともかく、前足の着地方法については短距離でもそうなんだと言いますし、サッカー選手の走り方も、高速な選手はそうだと言う人がいますね。川崎ではたとえば齋藤学が本気出して走っているときは、カカトついてないように僕には思えます。いつかスロー映像で確認したい。実際どうなんだろう。他の選手もそうなのかもしれないし、齋藤学も違うのかもしれず。

自分が走るときは、普通に何も考えずに走ると歩幅が狭くなるから、速く走りたいときはなるべく遠くに着地するためにヒザを使って前足を振ってカカトで着地することを意識していました。それは間違いだったのではないか? しかしこのツマサキ走法、自然にできるやついるのかな??

というわけで、こないだ走る機会があったときに、カカトをつかない走法を試してみました。その感想としては…確かに速いし、フォームが安定する。ヒザよりも大きな股関節を使って自然に足が大きく回転するのが気持ちいい。ただしふくらはぎの筋肉にはかなり負担がかかる。ちょっと走っただけなのにふくらはぎ部分の疲労の残りがハンパない。自分は脚部の筋力は割とある方だと勝手に思っていたのだけど…。あと足先の方向を安定させるためにこれまであまり使わなかった側部の筋肉が使われるということが分かった。

なるほどね。今の身体じゃ維持できないフォームということか。パワーもスタミナも足りない。特にふくらはぎ。

ふくらはぎの筋肉を鍛えるには、モノの本によればカルフレイズというトレーニングが手軽で有効なんだそうです。やり方を調べたら、確かに自宅でできる内容で、負荷もしっかりかかりそう。あとカルフレイズの動作は足首の可動域も改善するように思われる。だから練習としては時間があるときは走り込み、なければカルフレイズ、って感じのメニューになるか。

うーん、やり方の見当はついたけど。走力を上げようか、どうしようか、悩んでいます。正月休みに痛めた足首が直ったら、取り組める環境にはなるんだが…上げてどうするおっさんの走力。果たしてそれって…意味あんのん?

2019年。どうなるオレのふくらはぎ。←俳句です

川崎0-1ガンバ (終盤の失点癖)

この試合、始まる前は3-3の引き分けと予想していた。単にリーグ戦で0-0、1-1、2-2と来てたからさ。

しかし…くっそー!!

いろいろ問題はあると思うが、まずは終盤の失点ですよね。こないだ勝った試合でもかなりバタバタしてたよね。今シーズン、早くもこれでいくつめかな? と。

得点が少ないことに関しては、昔からプロサッカーの原理みたいなものとして認識しているものがあります。それはどんないい内容でも、どんな悪い内容でも、一人が何らかのスーパーなプレーをしなければ得点は入らない、ということ。そして、それをすれば入る。

今日はなかなか崩せなくて前半からミドルシュートが多かったが、あれがスーパーなミドルでバカスカ点が入ってた可能性だってあるわけよ。そしたら内容が同じでも超攻撃力すげー、相手は守備崩壊、みたいになってたであろうし。人の評価なんてそのくらい当てにならない。

そんな中で当てになるのは守備で相手を抑え込めてるか、といったあたり。今シーズンここまで、そこをどう見るか。確かに勝ててはいないし終盤の処理はアレだけど、あんまり心配する感じにはなってないんだよなー。

心配なのは、今日の終盤の、得点シーンではないですが、カウンターでFWに身体を当てられて出し抜かれたシーンが続いたところ。あれが川崎の弱点だと思われたら、今後辛くなるかもしれないっすね。

川崎1-0シドニー (川崎のstar)

会社を抜け出して等々力へ。まあ全席指定だったのでそれほど早く行く必要はないんですけどね。メインが良かったんですけど、S指定席にしました。この席割りは不評でしょう。AFCのアレとはいえ…

天候は快晴。ビールもうまい。しかし陽も落ちてくると、最終的にはかなり寒くなった。

スタメンには急成長中の田中碧、長谷川竜也が名前を連ねる。馬渡は怪我だよね? しかしマギーニョはどうしたの?? 右サイドには守田が配置された。

試合は序盤、相手が巧さを見せてくるが、徐々に川崎が試合を掌握していく。ただし得点がなかなか入らない。これで引き分けや負けを食らったら…歪んだ顔で「しょせんサッカーはキーパーゲー」を連呼してたとこでしたな。

守田の右サイドの攻撃はやはり停滞。逆に登里と長谷川というドリブラーを並べた左サイドは相手の守備を切り裂き続ける。長谷川は見るからにキレキレでしたね。どこかで守田を中央に移すか交代させるか…と思ってましたが、鈴木雄斗との交代でした。ほぼ同時にダミアン→知念(実際同時に出てきたんだけど、なんかの事情で知念は少し待たされた)。

最後はここまで攻撃を牽引してきた長谷川→齋藤学の交代。これは正直やばいんじゃないかと思ったんですけどね。いや私は今季は19番のユニ着てますけど、そこを代えて良くなる感じがしなかったんだよね。しかしそこはつい2年前までビッグクラブでキャプテン背負っていた男。少し考えてみてもらいたいんだけど、履歴書に「ビッグクラブでキャプテンやってました」って書いてあったら即採用しちゃうよね…そりゃ責任感が違うもん。キレッキレ状態の仲間/ライバルとの交代で入って仕事をしないなんてありえないわけです。まあ本人の言う通り、ゴール以外の場面で長谷川を上回るプレーを見せれたかというと疑問は残るわけだけど、今日はゴールが全てでしょう。おかげでクソゲーにならずに済んだ。まさにヒーロー。ありがとう川崎の星。

若手が成長し、交代選手も活躍。シーズン序盤に勝ち点を犠牲にしながらも順調に成長曲線を描いている今季の川崎。今後に非常に期待を持てますね。

川崎1-1鹿島 (いえいえいえいえいえ、うおうおうおうお)

金曜に会社を抜け出してスポーツ観戦のメッカ・等々力へ。年に一度のホーム鹿島戦です。シーズン序盤にこのカードはもったいない。チケットは当然、完売。激混みでした。そりゃTRFも来るわ。

平日にしては早めに着くようにうまく抜け出したんだけど、先着10000人に配られた野球ユニには全然、間に合わず。どうなってんだ川崎よ…ずいぶん遠くに来たもんだな。

試合の方は鹿島戦特有の緊張感に支配された、非常にピリッとした展開。流石に強いね。アジアチャンピオン。チャンスはあったから勝ちたかったところではあるが。

馬渡とマギーニョは恐らく日程と相談して併用する感じか。ここまで温存している山村の使い方をどうするのかは興味深いところ。ACLには連れてくんだろうとは思うが、プレーを見たかったという気持ちはあるね。

川崎0-0東京 (実質勝利でしょコレって)

いやー試合前は完全に負けると思ってましたわ。この過信と浮かれ具合。スタジアムをたゆたう、ゆるすぎる空気。何度経験してきたことか。こういうときにコロッと負けるのがフロンターレなんだよ。俺たち、何度も苦い思いをさせられてきたからさ、分かってるんだよね。

しかし何度でも「今回は違う」「今回からは違うんだ」と自分を奮い立たせて等々力に向かう自分。信じたい気持ち、裏切られ続けた経験。まあいい天気だったし、ちょっと寒いけどビール飲むにはいいかな、という気持ちもあった。

そのビールはセルフサービスになっていた。その手際はお世辞にも良いとは言えず、運営側は今頃は反省会をしてるだろう。自分もかなり戸惑いながらの操作になり、反省しております。泡は非常にクリーミーでした。ただ私のはサーバー内で盛大にこぼれました。50円引きとなる推奨タンブラーがフィットしないサイズを導入してしまったという致命的な欠陥も。私はここ10年ほどずっと紙コップ派ですけどね。まあ、そこは機械ですからベンダに不具合をちゃんとフィードバックすれば修正されると信じます。買う側も慣れてきたらまた変わるだろうし。

それでもブレイブサンダースは超絶可愛い売り子を歩き回らせての地ビール手売りに舵を切り、フロンターレは逆にセルフサービス側に振るというのは、何か対抗心でもあるんですかね。新たなる解、という感じで新鮮ではありました。いずれビール販売のスループットは上がりそうな感じはした。要は紙コップを650円で売って放っとけばいいって話ですからね。

スタジアム内は強風でした。この乾燥と強風…いい予感は全くしなかった。自宅付近とか、等々力緑地の他の場所ではあんまり吹いてなかったけど、ホームゴール裏から吹き抜けていく、等々力特有の川風ですね。

結果はスコアレスドロー。まあこれ負けずに勝ち点1を貰えたんでラッキーですよ。実質的には勝利と言っても過言ではないでしょう。普通は負けてるよこの展開。憲剛や小林悠のアレが決まってたら勝てた可能性すらある。フロンターレは変わった。勝者のメンターーリティーーー。それを感じさせられた1日だった。