2016-07-01 16 views
8

使用してdatetime.strptime()フォーマット文字列と日付文字列引数をミックスするのは非常によくある間違いです:ミキシングdatetime.strptime()の引数

datetime.strptime("%B %d, %Y", "January 8, 2014") 

の代わりに、他の方法で回避:

datetime.strptime("January 8, 2014", "%B %d, %Y") 

もちろん、それは実行時に失敗するでしょう:

>>> datetime.strptime("%B %d, %Y", "January 8, 2014") 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_strptime.py", line 325, in _strptime 
    (data_string, format)) 
ValueError: time data '%B %d, %Y' does not match format 'January 8, 2014' 

しかし、このプロをキャッチすることは可能ですかblem 静的に実際にコードを実行する前でさえ?それは何かpylintまたはflake8が助けることができますか?


PyCharmコード検査を試しましたが、どちらの警告も警告を出しません。おそらく、両方の引数が同じ型であるため、どちらも問題をより困難にする文字列です。文字列がdatetime形式の文字列かどうかを実際に解析する必要があります。また、Language Injections PyCharm/IDEA機能も関連しています。

+0

alecxe通常、文字列をdatetimeに変換する場合は、strptime以外の特定の文字列に対してstrptime()を使用します。指定された文字列が適切なdatetime形式であるかどうかを調べるには、チェックするためにもっと多くの正規表現パターンが必要になります。 – MicroPyramid

答えて

18

これは静的にチェックすることはできません。一般的なケースです。

次のスニペットを考えてみましょう:

d = datetime.strptime(read_date_from_network(), read_format_from_file()) 

このコードは完全に有効であり、両方read_date_from_networkread_format_from_fileが本当に適切な形式の文字列を返すかどこ - または彼らは、総ごみ、返却なしまたは一部の両方かもしれうんざりそれにかかわらず、その情報はしか実行時に決定できないため、静的チェッカーは無力です。


多くはである何、datetime.strptimeの現在の定義を考えると、我々はは静的型付け言語を使用してたとしても、我々は(非常に特殊なケースを除いて)このエラーをキャッチすることができないであろう - 理由はことを、この関数の署名が開始から私たちを運命づけされている:両方文字列は、

classmethod datetime.strptime(date_string, format) 
この定義では

date_stringformatであっても、彼らactuallかかわらず、 yには特別な意味があります。私たちは、このような静的型付け言語で類似した何かを持っていた場合でも:

public DateTime strpTime(String dateString, String format) 

コンパイラ(とリンターと皆を)まだだけ見ている:

public DateTime strpTime(String, String) 

次のいずれも識別可能でないことを意味しています互いに:

strpTime("%B %d, %Y", "January 8, 2014") // strpTime(String, String) CHECK 
strpTime("January 8, 2014", "%B %d, %Y") // strpTime(String, String) CHECK 
strpTime("cat", "bat") // strpTime(String, String) CHECK 

これで行うことができないと言っているわけではありませんすべて - Java/C++ /などのような静的型付けされた言語のためのいくつかのリンターが存在します。文字列リテラルをprintfなどの特定の関数に渡すと、その文字列リテラルを検査します。)、これはリテラル形式の文字列で直接その関数を呼び出すときにのみ行うことができます。私が提示した最初のケ​​ースでは、同じ文字列が無力になります。文字列が正しい形式であるかどうかはまだ分かっていないからです。リンターすなわち

はこれについて警告することができる場合があります

// Linter regex-es the first argument, sees %B et. al., warns you 
strpTime("%B %d, %Y", "January 8, 2014") 

が、このことについて警告することができません。

strpTime(scanner.readLine(), scanner.readLine()) 

は今、同じのpython内に操作することができ私は関数がファーストクラスなので非常に便利だとは思っていませんので、私は簡単に(仮説的なPythonの)リンターを書くことができます:

f = datetime.strptime 
d = f("January 8, 2014", "%B %d, %Y") 

そして、私たちは再びかなりくっついています。


ボーナス:ここ

問題何が悪かったのかはdatetime.strptimeは、これらの文字列のそれぞれへの暗黙的な意味を与えるということですが、それは型システムにその情報を表面化しません。可能なことは、2つの弦に異なるタイプを与えることでした。それで、使いやすさを犠牲にしてより安全性が向上しました。

例えば(使用PEP 484型注釈、a real thing!):

class DateString(str): 
    pass 

class FormatString(str): 
    pass 

class datetime(date): 
    ... 
    def strptime(date_string: DateString, format: FormatString) -> datetime: 
    # etc. etc. 

そして、それは一般的なケースでは良いリンティングを提供することが可能であることを開始する - DateStringのとformatStringのクラスが世話をする必要があるでしょうけれどもタイプ・システムはそのレベルで何もできないため、入力を検証する必要があります。


あとがき:

私はこれに対処するための最良の方法は、特定のDateTimeオブジェクトにバインドし、ちょうどフォーマット文字列を引数に取るさstrftime方法を、使用して問題を回避することだと思います。これは、私たちがそれを抱きしめたときに私たちを切断しない関数シグネチャを与えることによって、問題全体を回避します。わーい。

関連する問題