かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Python3 クロージャやってみた

次のような、年が変わった時だけ年数が入っている日付のデータを datetime でフォーマットしたかった。

data = [
  "2012年10月8日",
  "10月15日",
  # … 
  "12月24日",
  "2013年1月7日",
  "1月14日",
  # …
  "12月26日",
  "2014年1月9日",
  "1月16日",
  # …  
]

年数を global 変数に置いておいて \d+年 が出現する度に変数を更新して使えば済むのだけど、global の変数を更新していくのはイケてない感があります。年数内部で保持できるクロージャにできるとスッキリしそうです。

クロージャーを使ってフォーマットする

from re import search

def froamt(_year):
  year = _year

  def _format(date):
    nonlocal year

    match = search(r'\d+年', date)
    if match:
      year = match.group()
      ymd = date
    else:
      ymd = f'{year}{date}'

    return ymd

  return _format

START_YEAR = '2012年'
f = froamt(START_YEAR)
format_data = [f(v) for v in data]

これで全て YYYY年M月D日 という形式に変換することができました。
Python のスコープはまだよく解ってないのですが、クロージャーの内側の関数の中で外の関数で定義された変数を更新するには nonlocal キーワードでアクセスできるように宣言してあげる必要がありました。

datetime で変換する

データが全て YYYY年M月D日 と言う形にできたので、これを datetime.date でオブジェクトにして strftime でフォーマットしてあげれば良さそうです。
フォーマットする関数を追加します。

from re import search, split
from datetime import date as dt

def format_date(date_str):
  date = dt(*[int(n) for n in split('[年月日]', date_str) if n])
  return date.strftime('%Y-%m-%d (%a)')

def froamt(_year):
  year = _year

  def _format(date):
    nonlocal year

    match = search(r'\d+年', date)
    if match:
      year = match.group()
      ymd = date
    else:
      ymd = f'{year}{date}'
    
    # datetime でフォーマットしたものを返す
    return format_date(ymd)

  return _format

START_YEAR = '2012年'
f = froamt(START_YEAR)
format_data = [f(v) for v in data]

フォーマット関数内のフォーマット方法を変えれば好きな形にフォーマットできます。
これで YYYY-MM-DD (What day of the week) の形のデータに整形することができました!

date オブジェクト化するのにYYYY年M月D日 な文字列を配列にして datetime.date(year, month, date) でオブジェクト化しているのですが、もっとスマートな変換方法がありそうな気がしています。

所感

Python でも 関数を返す関数を作成できることが判ったので良かったです。(個人的に PythonJavaScript 似てるな〜って感じてます。)
クロージャーの内部の関数から外部の変数にアクセスするのに nonlocal 宣言が必要で、このあたりのスコープまわりは理解が足りてないので引き続き調べて理解しておきたいです。


[参考]

ゼロから作るDeep Learning ❷ ―自然言語処理編

ゼロから作るDeep Learning ❷ ―自然言語処理編

  • 作者:斎藤 康毅
  • 発売日: 2018/07/21
  • メディア: 単行本(ソフトカバー)

アイカツ!1stシーズン Blu-ray BOX1

アイカツ!1stシーズン Blu-ray BOX1

  • 発売日: 2014/11/05
  • メディア: Blu-ray

日付でお察しの方が居るかもですがアイカツ! 放送日のリストでした