2016-07-01 33 views
8

E 'piuttosto un errore comune a mescolare la stringa e stringa data argomenti formato datetime.strptime() utilizzando:miscelazione datetime.strptime() argomenti

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

invece il contrario:

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

Certamente, non funzionerebbe durante il runtime:

>>> 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' 

Ma, è possibile prendere questo pro blem staticamente prima ancora di eseguire effettivamente il codice? È qualcosa che può aiutare con pylint o flake8?


Ho provato l'ispezione del codice PyCharm, ma entrambi gli snippet non emettono alcun avviso. Probabilmente, perché entrambi gli argomenti hanno lo stesso tipo - entrambi sono stringhe che rendono il problema più difficile. Dovremmo analizzare in realtà se una stringa è una stringa di formato datetime o meno. Inoltre, la funzione PyCharm/IDEA Language Injections sembra pertinente.

+0

alecxe, in genere se vogliamo convertire una stringa in data/ora usiamo strptime() su una stringa particolare, a parte strptime possiamo controllare la stringa con espressioni regolari se la stringa data è in formato datetime appropriato o meno, ma prenderebbe modelli di espressioni più regolari da controllare. – MicroPyramid

risposta

18

Io sostengo che questo non può essere controllato staticamente nel caso generale.

Si consideri il seguente frammento di codice:

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

Questo codice può essere completamente valida, dove entrambi read_date_from_network e read_format_from_file davvero corde del formato corretto tornare - o possono essere spazzatura totale, sia di ritorno Nessuno o un po ' una schifezza. Indipendentemente da ciò, tale informazione può solo essere determinata in fase di esecuzione - quindi, un controllo statico è impotente.


Cosa c'è di più, dato l'attuale definizione di datetime.strptime, anche se siamo stati utilizzando un linguaggio a tipizzazione statica, non saremmo in grado di catturare questo errore (tranne in casi molto specifici) - il motivo è che la firma di questa funzione ci ha condannata fin dall'inizio:

classmethod datetime.strptime(date_string, format) 

in questa definizione, date_string e format sono entrambi stringhe, anche se actuall hai un significato speciale Anche se avessimo qualcosa di analogo in un linguaggio a tipizzazione statica come questo:

public DateTime strpTime(String dateString, String format) 

Il compilatore (e linter e tutti gli altri) ancora vede solo:

public DateTime strpTime(String, String) 

Il che significa che nessuno dei seguenti sono distinguibili gli uni dagli altri:

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 

Questo non vuol dire che non può essere fatto a tutti - esistono alcuni linters per linguaggi tipizzati staticamente come Java/C++/etc. che controllerà i letterali stringa quando li passi ad alcune funzioni specifiche (come printf, ecc.), ma questo può essere fatto solo quando si chiama quella funzione direttamente con una stringa di formato letterale. Gli stessi ltri diventano impotenti nel primo caso che ho presentato, perché semplicemente non è ancora noto se le stringhe saranno nel formato giusto.

cioè A linter possono essere in grado di mettere in guardia su questo:

// Linter regex-es the first argument, sees %B et. al., warns you 
strpTime("%B %d, %Y", "January 8, 2014") 

ma non sarebbe in grado di mettere in guardia su questo:

strpTime(scanner.readLine(), scanner.readLine()) 

Ora, lo stesso potrebbe essere progettato in un pitone linter, ma non credo che sarebbe molto utile perché le funzioni sono di prima classe, quindi potrei facilmente sconfiggere il lintero (ipotetico pitone) scrivendo:

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

E poi siamo di nuovo in crisi.


Bonus: What Went Wrong

Il problema qui è che il datetime.strptime dà significato implicito di ciascuna di queste stringhe, ma non superficiale che informazioni al sistema tipo. Quello che si sarebbe potuto fare era dare alle due stringhe tipi diversi, quindi ci sarebbe stata maggiore sicurezza, anche se a scapito di una certa facilità d'uso.

esempio (usando PEP 484 annotazioni di tipo, a real thing!):

class DateString(str): 
    pass 

class FormatString(str): 
    pass 

class datetime(date): 
    ... 
    def strptime(date_string: DateString, format: FormatString) -> datetime: 
    # etc. etc. 

Poi avrebbe cominciato a essere possibile fornire una buona linting nel caso generale - anche se il DateString e classi FormatString avrebbero bisogno di prendersi cura di convalidare il loro input, perché ancora una volta, il sistema di tipi non può fare nulla a quel livello.


Postfazione:

Credo che il modo migliore per affrontare questo è di evitare il problema utilizzando il metodo strftime, che è legato a un oggetto datetime specifico e richiede solo un argomento stringa di formato. Ciò aggira l'intero problema dandoci una firma di funzione che non ci taglia quando la abbracciamo. Sìì.

Problemi correlati