2016-07-01 2 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' 

Но возможно ли это поймать? m статически еще до фактического запуска кода? Это может помочь pylint или flake8?


Я пробовал проверку кода PyCharm, но оба фрагмента не выдают никаких предупреждений. Вероятно, потому что оба аргумента имеют один и тот же тип - они оба являются строками, что затрудняет задачу. Мы должны были бы фактически проанализировать, является ли строка строкой формата даты или нет. Кроме того, функция PyCharm/IDEA Language Injections выглядит актуальной.

+0

alecxe, как правило, если мы хотим преобразовать строку в datetime, мы будем использовать strptime() для конкретной строки, кроме strptime, мы можем проверить строку с регулярными выражениями, если данная строка находится в правильном формате datetime или нет, но это потребуется больше шаблонов регулярных выражений для проверки. – MicroPyramid

ответ

18

Я утверждаю, что это не может быть проверено статически в общем случае.

Рассмотрим следующий фрагмент кода:

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

Этот код может быть полностью действительным, где оба read_date_from_network и read_format_from_file действительно возвращают строки в нужном формате - или они могут быть тотальным мусор, как возвращение None или некоторые дерьмо. Независимо от того, что информация может быть определена только, будет определена во время работы - следовательно, статическая проверка бесполезна.


Более того, учитывая нынешнее определение datetime.strptime, даже если мы были с использованием статически типизированный язык, мы не смогли бы поймать эту ошибку (за исключением очень специфических случаев) - причина в том, что сигнатуру этой функции обречено нас с самого начала:

classmethod datetime.strptime(date_string, format) 

в этом определении, date_string и format оба струны, несмотря на то, что они на самом деле имеют особое значение. Даже если мы что-то аналогичное в статически типизированных языках, как это:

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()) 

Теперь, то же самое можно сконструировать в питона linter, но я не считаю, что это было бы очень полезно, потому что функции были первоклассными, поэтому я мог легко победить (гипотетический python) linter, написав:

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

А потом мы снова снова очень хорошо.


Бонус: Что пошло не так

Проблема здесь состоит в том, что datetime.strptime дает скрытое значение для каждой из этих строк, но не на поверхность этой информации в систему типа. Что можно было сделать, так это дать двум строкам разные типы - тогда могла бы быть больше безопасности, хотя бы за счет некоторой простоты использования.

например (с использованием ПЭП 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 должны были бы заботиться проверки их ввода, потому что снова система типов не может ничего сделать на этом уровне.


Послесловие:

Я думаю, что лучший способ справиться с этим, чтобы избежать этой проблемы, используя strftime метод, который привязан к конкретному объекту даты и времени, и принимает только строковый формат аргумента. Это обходит всю проблему, предоставляя нам сигнатуру функции, которая не режет нас, когда мы обнимаем ее. Ура.