混合 datetime.strptime() 参数

2024-03-09

混淆是一个很常见的错误datetime.strptime() https://docs.python.org/2/library/datetime.html#datetime.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 or flake8可以帮忙吗?


我已经尝试过 PyCharm 代码检查,但两个片段都没有发出任何警告。可能是因为两个参数具有相同的类型 - 它们都是字符串,这使得问题变得更加困难。我们必须实际分析字符串是否是日期时间格式字符串。另外,语言注入 https://www.jetbrains.com/help/idea/2016.1/using-language-injections.htmlPyCharm/IDEA 功能看起来很相关。


我声称这不能静态检查在一般情况下.

考虑以下片段:

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

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


更重要的是,考虑到 datetime.strptime 的当前定义,即使我们were使用静态类型语言,我们将无法捕获此错误(除非在非常特殊的情况下)——原因是这个函数的签名从一开始就注定了我们的失败:

classmethod datetime.strptime(date_string, format)

在这个定义中,date_string and format都是strings,尽管它们实际上具有特殊含义。即使我们在静态类型语言中有类似的东西,如下所示:

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 类型注释,真实的东西! https://www.python.org/dev/peps/pep-0484/):

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 https://docs.python.org/2/library/datetime.html#datetime.datetime.strftime方法,它绑定到特定的日期时间对象,并且仅接受格式字符串参数。这通过给我们一个函数签名来规避整个问题,当我们拥抱它时不会割伤我们。耶。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

混合 datetime.strptime() 参数 的相关文章

随机推荐