はじめてのPython

2019 T.Takeda

知っておくべきいくつかのこと

  1. インタプリタ言語
  2. 動的型付け言語、強い型付け
  3. ガベージコレクションを持つ
  4. Python2と3は別物
  5. Pythonのパフォーマンス
  6. Python/pandasの注意点

インタプリタ言語

実行時に解釈され、順に実行される

  • コンパイルしないため、実行速度が遅い
  • 動かすまで何が起こるかわからない怖さ

強い動的型付け言語

実行時に型が決定される言語

  • 動的で危ないことを認識する
  • いつの間にか数値が文字列になったりはしない
  • プログラミング初心者には使いやすいが品質担保が難しいことを理解する
💡型アノテーションは単なる注釈で機能的な意味は薄い

ガベージコレクション(GC)

PythonはGCを備えており、メモリ制御を明示的に行う必要がない。

1. いつ確保されて、いつ解放するか理解して利用する必要がある
2. delしたり、関数スコープから出るときにオーバーヘッドがあることを意識する
3. 知らないと見えないところで遅くなる
  • 解放されるタイミングは任意だが、参照が残るとメモリリークする
💡GC呼び出しを手動でやるときは何かおかしいので思いとどまる

オフサイドルール

  • インデントが構文規則であり、揃える必要がある
  • インデントがずれてもオフサイドルールが合っているとエラーはしないので注意
💡リファクタリング時にずれることがあるので特に注意が必要

Python2/3

  • Python2はUnicodeという病により死んだ
  • Python3のことを知ろう
💡新しいものは全部Python3.xで始める

Cython

  • Pythonは動作パフォーマンスが悪い言語
  • パフォーマンスが必要な箇所では、C/C++/Fortranで動いている

Pythonの流行

研究/学術目的で使いやすかった

  • 数値計算ライブラリnumpyを筆頭にscipy、pandas、matplotlibなどの存在
💡機械学習が後押しし、研究から実用に転じた印象

Haskellに習う

Python2はHaskellを参考にしたにも関わらず、関数型のパラダイムをいち早く取り込んだだけで終わった

Pythonパフォーマンス改善

for文より、リスト内包表記

これで歩行者が競歩くらい速くなる
res = []
for item in items:
  res.append({
    'piyo': item
  })

res = [{'piyo': item} for item in items]
💡set, dictなどジェネレータを呼び出すものも同様
💡list()ではなく、内包表記を推奨

Pythonパフォーマンス改善

繰り返しの参照は変数に代入

これで競歩が自転車くらい速くなる
for item in items:
  self.validation(item)

validation = self.validation
for item in items:
  validation(item)
💡参照、関数呼び出しのオーバーヘッドはかなり重い
💡やり過ぎると可読性を下げる

Pythonパフォーマンス改善

定数定義はループの外に

これで自転車が原付バイクくらい速くなる
for item in items:
  init_val = {
    'name': ini.get('section', 'name')
  }

name = ini.get('section', 'name')
for item in items:
  init_val = {
    'name': name
  }
💡最適化されずに毎回同じ計算が走ることを回避

Pythonパフォーマンス改善

インスタンス作成はループの外に

これで原付バイクが大型バイクくらい速くなる
event = { ... }
for item in items:
  data = Data(event)
  data.select(item)
  ...

event = { ... }
data = Data(event)
for item in items:
  data.select(item)
  ...
💡オブジェクトの生成コストは相応に大きいため、スコープに閉じて再生成させない

Pythonの注意点

raiseとraise eは別物

try:
  ret = sugoi_kinou()
except Exception as e:
  raise e

try:
  ret = sugoi_kinou()
except Exception:
  raise
💡raise eは後続に渡すexception内容を自身以降に限定する

Pythonの注意点

意図もなくマングリングしない

  • 名前空間の衝突を回避する場合にのみ使う
def __process():
  yeah = party(people)

def _process():
  yeah = party(people)
💡アンダースコア一つはプライベートに使う関数/変数の意味

Pythonの注意点

参照渡しと値渡しはある

  • 型により挙動が変わる
  • 暗黙的に受け取り先で値を書き換えてはいけない(副作用)
def func(args):
    ...

n = 22
func(n) # 値渡し

l = [0, 1, 2]
func(l) # 参照渡し
💡プリミティブ型は値渡し、リストや辞書は参照渡し

pandasの注意点

DataFrameの要素をループしない

これで亀がスペースシャトルくらいになる
for index, row in df.iterrows():
  row['sum'] = row['val1'] + row['val2']

df['sum'] = df['val1'] + df['val2']
💡pandasに限らず、関数型パラダイムを持つ手続き型言語でよく見られる
💡iterrows/ilocを使う場合は、本当にDataFrameである必要があるか考える

pandasの注意点

同様にSeriesをループしていることがある

  • apply/mapを使う
💡tolist()するなら最初からリストでやった方がよい場合もある
💡DataFrameとListの行き来もオーバーヘッドが大きい

pandasの注意点

filterするなら先にやる

df.apply(func).filter(cond)

df.filter(cond).apply(func)
💡pandasに限らず手続き型では無駄な処理が減るので、処理したいデータに先に絞る

Pythonの注意点

バックスラッシュで改行、括弧内で改行

  • チームで統一しよう
res = df.filter() \
        .apply()
res = (df.filter()
         .apply())
💡もちろん、好みもある
💡Pythonでは、PEP8を読むことが大事

昨今のPythonの使いどころ

Pythonの善し悪しを理解して使おう

  • クライアントサイドPython
    • DeepLearning/MachineLearning
    • Jupyter
  • サーバーサイドPython
    • Serverless(AWS Lambdaなど)
    • Django
💡よいPythonライフを!