Domanda

Ho una funzione Python che accetta un argomento numerico secondo cui deve essere un numero intero affinché si comporti correttamente. Qual è il modo preferito di verificarlo in Python?

La mia prima reazione è di fare qualcosa del genere:

def isInteger(n):
    return int(n) == n

Ma non posso fare a meno di pensare che questo sia 1) costoso 2) brutto e 3) soggetto alle tenere misericordie della macchina epsilon.

Python fornisce mezzi nativi per il controllo delle variabili? O è considerata una violazione del design tipizzato in modo dinamico del linguaggio?

MODIFICA: poiché alcune persone lo hanno chiesto: l'applicazione in questione funziona con prefissi IPv4, reperendo dati da file di testo semplici. Se qualsiasi input viene analizzato in un float, tale record deve essere visualizzato come non valido e ignorato.

È stato utile?

Soluzione

isinstance(n, int)

Se hai bisogno di sapere se è sicuramente un int reale e non una sottoclasse di int (generalmente non dovresti farlo):

type(n) is int

questo:

return int(n) == n

non è una buona idea, poiché i confronti tra tipi possono essere veri, in particolare int(3.0)==3.0

Altri suggerimenti

Sì, come ha detto Evan, non digitare check. Prova a usare il valore:

def myintfunction(value):
   """ Please pass an integer """
   return 2 + value

Che non ha un typecheck. È molto meglio! Vediamo cosa succede quando lo provo:

>>> myintfunction(5)
7

Funziona, perché è un numero intero. Hm. Proviamo un po 'di testo.

>>> myintfunction('text')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in myintfunction
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Mostra un errore, TypeError, che è comunque quello che dovrebbe fare. Se il chiamante vuole catturarlo, è possibile.

Cosa faresti se facessi un typecheck? Mostra un errore giusto? Quindi non è necessario digitare il segno di spunta perché l'errore si sta già manifestando automaticamente.

Inoltre, dato che non hai digitato il controllo, la tua funzione funziona con altri tipi:

Galleggianti:

>>> print myintfunction(2.2)
4.2

Numeri complessi:

>>> print myintfunction(5j)
(2+5j)

Decimali:

>>> import decimal
>>> myintfunction(decimal.Decimal('15'))
Decimal("17")

Anche oggetti completamente arbitrari che possono aggiungere numeri!

>>> class MyAdderClass(object):
...     def __radd__(self, value):
...             print 'got some value: ', value
...             return 25
... 
>>> m = MyAdderClass()
>>> print myintfunction(m)
got some value:  2
25

Quindi non si ottiene chiaramente nulla digitando il segno di spunta. E perdere molto.


UPDATE:

Da quando hai modificato la domanda, è ora chiaro che l'applicazione chiama una routine upstream che ha senso solo con ints.

Stando così le cose, penso ancora che dovresti passare il parametro come ricevuto alla funzione upstream. La funzione upstream lo gestirà correttamente, ad es. generando un errore, se necessario. Dubito fortemente che la tua funzione relativa agli IP si comporti in modo strano se passi un float. Se puoi darci il nome della biblioteca, possiamo controllarlo per te.

Ma ... Se la funzione upstream si comporterà in modo errato e ucciderà alcuni bambini se gli passi un float (ne dubito fortemente), allora basta chiamare int() su di esso:

def myintfunction(value):
   """ Please pass an integer """
   return upstreamfunction(int(value))

Non stai ancora controllando i caratteri, quindi otterrai la maggior parte dei vantaggi di non controllarli.


Se anche dopo tutto questo, vuoi davvero digitare check, nonostante riduca la leggibilità e le prestazioni della tua applicazione per nessun beneficio, usa un assert per farlo.

assert isinstance(...)
assert type() is xxxx

In questo modo possiamo disattivare <sarcasm> se rimuovere questa </sarcasm> funzionalità <=> dal programma chiamandola come

python -OO program.py
if type(n) is int

Controlla se n è un int Python e solo un int. Non accetta sottoclassi di int.

Il controllo del tipo, tuttavia, non si adatta alla " Python way " ;. È meglio utilizzare <=> come int e, se genera un'eccezione, catturarlo e agire su di esso.

Python ora supporta digitazione graduale tramite modulo di battitura e mypy. Il modulo typing fa parte di stdlib a partire da Python 3.5 e può essere scaricato da PyPi se hai bisogno di backport per Python 2 o versione precedente di Python 3. Puoi installare mypy eseguendo pip install mypy dalla riga di comando.

In breve, se si desidera verificare che alcune funzioni accettino un int, un float e restituiscano una stringa, annotare la funzione in questo modo:

def foo(param1: int, param2: float) -> str:
    return "testing {0} {1}".format(param1, param2)

Se il tuo file è stato chiamato test.py, puoi quindi digitare la casella dopo aver installato mypy eseguendo mypy test.py dalla riga di comando.

Se stai utilizzando una versione precedente di Python senza supporto per le annotazioni delle funzioni, puoi utilizzare i commenti di tipo per ottenere lo stesso effetto:

def foo(param1, param2):
    # type: (int, float) -> str
    return "testing {0} {1}".format(param1, param2)

Si utilizza lo stesso comando mypy --py2 test.py per i file Python 3 e isinstance per i file Python 2.

Le annotazioni del tipo vengono ignorate interamente dall'interprete Python in fase di runtime, quindi impongono un sovraccarico minimo o nullo: il normale flusso di lavoro consiste nel lavorare sul codice ed eseguire periodicamente il mypy per rilevare errori ed errori. Alcuni IDE, come PyCharm, comprendono i suggerimenti sul tipo e possono avvisarti di problemi e digitare discrepanze nel codice durante la modifica diretta.

Se, per qualche motivo, hai bisogno di verificare i tipi in fase di esecuzione (forse devi convalidare molti input?), dovresti seguire i consigli elencati nelle altre risposte - ad es. usa issubclass, <=> e simili. Ci sono anche alcune librerie come imporre che tentano di eseguire il controllo dei caratteri (rispettando le annotazioni dei tipi) in fase di esecuzione, anche se non sono sicuro di quanto siano pronti per la produzione al momento della stesura.

Per ulteriori informazioni e dettagli, consultare il sito Web mypy , il FAQ mypy e PEP 484 .

Non digitare check. Il punto fondamentale della digitazione dell'anatra è che non dovresti. Ad esempio, cosa succede se qualcuno ha fatto qualcosa del genere:

class MyInt(int):
    # ... extra stuff ...

Programmare in Python ed eseguire il controllo dei caratteri come si potrebbe in altre lingue sembra come scegliere un cacciavite con cui battere un chiodo. È più elegante utilizzare le funzionalità di gestione delle eccezioni di Python.

Da una riga di comando interattiva, è possibile eseguire un'istruzione come:

int('sometext')

Questo genererà un errore - ipython mi dice:

<type 'exceptions.ValueError'>: invalid literal for int() with base 10: 'sometext'

Ora puoi scrivere del codice come:

try:
   int(myvar) + 50
except ValueError:
   print "Not a number"

Che può essere personalizzato per eseguire qualsiasi operazione sia richiesta E per rilevare eventuali errori previsti. Sembra un po 'contorto ma si adatta alla sintassi e ai modi di dire di Python e risulta in un codice molto leggibile (una volta che ti sei abituato a parlare in Python).

Sarei tentato di qualcosa come:

def check_and_convert(x):
    x = int(x)
    assert 0 <= x <= 255, "must be between 0 and 255 (inclusive)"
    return x

class IPv4(object):
    """IPv4 CIDR prefixes is A.B.C.D/E where A-D are 
       integers in the range 0-255, and E is an int 
       in the range 0-32."""

    def __init__(self, a, b, c, d, e=0):
        self.a = check_and_convert(a)
        self.b = check_and_convert(a)
        self.c = check_and_convert(a)
        self.d = check_and_convert(a)
        assert 0 <= x <= 32, "must be between 0 and 32 (inclusive)"
        self.e = int(e)

In questo modo quando lo stai usando, qualsiasi cosa può essere passata, ma memorizzi solo un numero intero valido.

che ne dici di:

def ip(string):
    subs = string.split('.')
    if len(subs) != 4:
        raise ValueError("incorrect input")
    out = tuple(int(v) for v in subs if 0 <= int(v) <= 255)
    if len(out) != 4:
        raise ValueError("incorrect input")
    return out

ovviamente c'è la funzione isinstance standard (3, int) ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top