混合datetime.strptime()参数

时间:2016-07-01 14:26:51

标签: python datetime pycharm pylint static-code-analysis

使用以下方法混合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'

但是,即使在实际运行代码之前,是否有可能静态地捕获静态这个问题?是pylint还是flake8可以提供帮助?

我已经尝试过PyCharm代码检查,但两个代码段都没有发出任何警告。可能,因为两个参数都具有相同的类型 - 它们都是字符串,这使得问题更加困难。我们必须实际分析字符串是否是日期时间格式字符串。此外,Language Injections PyCharm / IDEA功能看起来很相似。

1 个答案:

答案 0 :(得分:17)

我声称无法在一般情况下静态检查

请考虑以下代码段:

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

此代码可能完全有效,其中read_date_from_networkread_format_from_file确实返回正确格式的字符串 - 或者它们可能是完全垃圾,都返回None或某些废话。无论如何,该信息只能 在运行时确定 - 因此,静态检查器无能为力。

更重要的是,鉴于datetime.strptime的当前定义,即使我们 使用静态类型语言,我们也无法捕获此错误(除非在非常具体的情况下) - - 原因是此功能的签名从一开始就注定了我们

classmethod datetime.strptime(date_string, format)

在此定义中,date_stringformat都是字符串,即使它们实际上具有特殊含义。即使我们在这种静态类型的语言中有类似的东西:

public DateTime strpTime(String dateString, String format)

编译器(以及linter和其他所有人)仍然只看到:

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 linter,但我不相信它会非常有用,因为函数是一流的,所以我可以通过编写来轻松击败(假设的python)linter:

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

然后我们再次受到冲击。

奖金:错在哪里

这里的问题是datetime.strptime给每个字符串赋予隐含意义,但它没有将该信息表示给类型系统。可以做的是给两个不同类型的字符串 - 然后可以有更多的安全性,虽然以牺牲一些易用性为代价。

例如(使用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.

然后在一般情况下提供良好的linting开始是可行的 - 尽管DateString和FormatString类需要负责验证他们的输入,因为类型系统不能在那个级别做任何事情

后记:

我认为处理此问题的最佳方法是使用strftime方法来避免此问题,该方法绑定到特定的日期时间对象并仅采用格式字符串参数。通过给我们一个功能签名来避开整个问题,当我们拥抱它时,它不会削弱我们。耶。