混合 datetime.strptime() 参数
Mixing datetime.strptime() arguments
混淆 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 功能看起来很相关。
我声称这不能静态检查在一般情况下。
考虑以下片段:
d = datetime.strptime(read_date_from_network(), read_format_from_file())
这段代码可能是完全有效的,其中 read_date_from_network
和 read_format_from_file
确实 return 正确格式的字符串——或者它们可能完全是垃圾,都 returning None 或一些废话。无论如何,该信息只能 在运行时确定——因此,静态检查器是无能为力的。
此外,鉴于 datetime.strptime 的当前定义,即使我们 是 使用静态类型语言,我们也无法捕获此错误 (除非在非常特殊的情况下)——原因是这个函数的签名从一开始就注定了我们:
classmethod datetime.strptime(date_string, format)
在这个定义中,date_string
和format
都是字符串,尽管它们实际上有特殊的含义。即使我们在像这样的静态类型语言中有类似的东西:
public DateTime strpTime(String dateString, String format)
编译器(以及 linter 和其他所有人)仍然只看到:
public DateTime strpTime(String, String)
这意味着以下 none 个是可区分的:
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
这并不是说它根本无法完成——确实存在一些针对静态类型语言的 linter,例如 Java/C++/等。当您将字符串文字传递给某些特定函数(如 printf 等)时,它将检查字符串文字,但这只能在您直接使用文字格式字符串调用该函数时才能完成。在我介绍的第一个案例中,相同的 linters 变得同样无助,因为还不知道字符串的格式是否正确。
即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,但我认为它不会很有用,因为函数是 first-class,所以我可以轻松击败(假设的 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 classes 需要注意验证它们的输入,因为同样,类型系统可以在那个级别做任何事情。
后记:
我认为处理此问题的最佳方法是使用 strftime
方法来避免此问题,该方法绑定到特定的日期时间对象并且仅采用格式字符串参数。这通过给我们一个函数签名来绕过整个问题,当我们拥抱它时不会伤害我们。耶。
混淆 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 功能看起来很相关。
我声称这不能静态检查在一般情况下。
考虑以下片段:
d = datetime.strptime(read_date_from_network(), read_format_from_file())
这段代码可能是完全有效的,其中 read_date_from_network
和 read_format_from_file
确实 return 正确格式的字符串——或者它们可能完全是垃圾,都 returning None 或一些废话。无论如何,该信息只能 在运行时确定——因此,静态检查器是无能为力的。
此外,鉴于 datetime.strptime 的当前定义,即使我们 是 使用静态类型语言,我们也无法捕获此错误 (除非在非常特殊的情况下)——原因是这个函数的签名从一开始就注定了我们:
classmethod datetime.strptime(date_string, format)
在这个定义中,date_string
和format
都是字符串,尽管它们实际上有特殊的含义。即使我们在像这样的静态类型语言中有类似的东西:
public DateTime strpTime(String dateString, String format)
编译器(以及 linter 和其他所有人)仍然只看到:
public DateTime strpTime(String, String)
这意味着以下 none 个是可区分的:
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
这并不是说它根本无法完成——确实存在一些针对静态类型语言的 linter,例如 Java/C++/等。当您将字符串文字传递给某些特定函数(如 printf 等)时,它将检查字符串文字,但这只能在您直接使用文字格式字符串调用该函数时才能完成。在我介绍的第一个案例中,相同的 linters 变得同样无助,因为还不知道字符串的格式是否正确。
即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,但我认为它不会很有用,因为函数是 first-class,所以我可以轻松击败(假设的 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 classes 需要注意验证它们的输入,因为同样,类型系统可以在那个级别做任何事情。
后记:
我认为处理此问题的最佳方法是使用 strftime
方法来避免此问题,该方法绑定到特定的日期时间对象并且仅采用格式字符串参数。这通过给我们一个函数签名来绕过整个问题,当我们拥抱它时不会伤害我们。耶。