きっかけは紀伊國屋書店
- 作者: アンドリュー・フィリップス,ネルミン・セリフォヴィック,竹添直樹,島本多可子
- 出版社/メーカー: 翔泳社
- 発売日: 2016/02/05
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Java™ Puzzlers: Traps, Pitfalls, and Corner Cases
- 作者: Joshua Bloch,Neal Gafter
- 出版社/メーカー: Addison-Wesley Professional
- 発売日: 2005/06/24
- メディア: Kindle版
- この商品を含むブログを見る
『Effective Python』や『Python文法詳解』で書かれている「落とし穴」みたいなものを集めてみるのも面白そうと思い、 デフォルト引数にまつわるもので1つ試作してみた。
Puzzle 1: Once Read
函数get_unixtime
は時刻をUNIXタイムスタンプに変換するものである。
引数now
はデフォルト引数として現在時刻を与えている。
以下のプログラムを実行すると、どのような出力になるか。
import datetime import time def get_unixtime(now=datetime.datetime.now()): time_tuple = now.timetuple() utc_unixtime = time.mktime(time_tuple) return utc_unixtime print(get_unixtime()) time.sleep(1) print(get_unixtime())
Solution 1: Once Read
プログラムを実行すると次のようになる。
1455710144.0 1455710144.0
time.sleep
で1秒間を開けているのでUNIXタイムスタンプも1秒ほど進むはずだが、全く同じ結果が出力された。
これは、函数get_unixtime
のデフォルト引数は函数オブジェクトが作成されるタイミングで1回のみ実行されるためである。
そのため、何度デフォルト引数のまま実行しても函数オブジェクトが作成された時刻のUNIXスタンプを返す。
クラスではどうなるのか。
import datetime import time import pytz class TimeZoneTranser(object): def __init__(self, now=datetime.datetime.now(), local_tz=pytz.timezone("Asia/Tokyo")): self.now = now self.local_tz = local_tz self.local_dt = self.local_tz.localize(self.now) self.utc_dt = pytz.utc.normalize(self.local_dt.astimezone(pytz.utc)) def translate(self, target_tz=pytz.timezone("US/Pacific")): self.target_dt = target_tz.normalize(self.utc_dt.astimezone(target_tz)) return self.target_dt tr1 = TimeZoneTranser() time.sleep(1) tr2 = TimeZoneTranser() print(tr1.translate()) print(tr2.translate())
2016-02-17 04:42:11.063276-08:00 2016-02-17 04:42:11.063276-08:00
これもまた、1秒ずれた出力を期待されていたが同一の結果が返される。
__init__()
の引数でミュータブルなデフォルト引数を与えてしまうとクラスのインスタンス間で共通の値を参照してしまうのである。
対策は、『Effective Python』 項目20の通り、None
を使う。
import datetime import time def get_unixtime(now=None): now = datetime.datetime.now() if now is None else now time_tuple = now.timetuple() utc_unixtime = time.mktime(time_tuple) return utc_unixtime print(get_unixtime()) time.sleep(1) print(get_unixtime())
1455716364.0 1455716365.0
なお、『Effective Python』 項目20にはドキュメンテーション文字列で振る舞いを文書化するよう指示している。