【问题标题】:Mixing datetime.strptime() arguments混合 datetime.strptime() 参数
【发布时间】:2016-11-04 00:20:15
【问题描述】:

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'

但是,是否有可能在实际运行代码之前静态地捕捉到这个问题? pylintflake8 可以帮忙吗?


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

【问题讨论】:

  • alecxe,通常如果我们想将字符串转换为日期时间,我们会在特定字符串上使用 strptime(),除了 strptime,如果给定的字符串是正确的日期时间格式,我们可以使用正则表达式检查字符串与否,但需要更多的正则表达式模式来检查。

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


【解决方案1】:

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

考虑以下 sn-p:

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++/等)的 linter。当您将字符串文字传递给某些特定函数(如 printf 等)时,它将检查字符串文字,但这只能在您直接使用文字格式字符串调用该函数时完成。在我提出的第一种情况下,同样的 linter 变得同样无助,因为它根本不知道字符串是否是正确的格式。

即linter 可能会对此发出警告:

// 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 方法来避免该问题,该方法绑定到特定的日期时间对象并且只接受格式字符串参数。这通过给我们一个函数签名来规避整个问题,当我们拥抱它时不会伤害我们。耶。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-18
    • 2012-02-10
    • 2018-06-03
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 2021-05-01
    • 2016-06-24
    相关资源
    最近更新 更多