J'ai une fonction Python qui prend un argument numérique qui doit être un entier pour qu'il se comporte correctement. Quel est le moyen préféré de vérifier cela en Python?
Ma première réaction est de faire quelque chose comme ça:
def isInteger(n):
return int(n) == n
Mais je ne peux pas m'empêcher de penser que c'est 1) cher 2) laid et 3) soumis à la tendresse de la machine epsilon.
Python fournit-il des moyens natifs de vérification de type des variables? Ou est-ce considéré comme une violation de la conception typée dynamiquement du langage?
EDIT: puisque plusieurs personnes l’ont demandé - l’application en question fonctionne avec des préfixes IPv4, en extrayant des données à partir de fichiers texte plats. Si une entrée est analysée dans un float, cet enregistrement doit être considéré comme mal formé et ignoré.
isinstance(n, int)
Si vous avez besoin de savoir s’il s’agit bien d’un int réel et non d’une sous-classe d’int (en général, vous n’auriez pas besoin de le faire):
type(n) is int
ce:
return int(n) == n
n'est pas une si bonne idée, car les comparaisons entre types peuvent être vraies - notamment int(3.0)==3.0
Oui, comme dit Evan, ne tapez pas check. Essayez juste d'utiliser la valeur:
def myintfunction(value):
""" Please pass an integer """
return 2 + value
Cela n'a pas de chèque. C'est beaucoup mieux! Voyons ce qui se passe quand je l'essaie:
>>> myintfunction(5)
7
Cela fonctionne, car c'est un entier. Hm. Essayons du texte.
>>> 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'
Il affiche une erreur, TypeError, qui est ce qu’elle devrait faire de toute façon. Si l'appelant veut intercepter cela, c'est possible.
Que feriez-vous si vous faisiez un typecheck? Afficher une erreur non? Donc, vous n’avez pas à taper à la machine car l’erreur apparaît déjà automatiquement.
De plus, depuis que vous n'avez pas tapé check, votre fonction fonctionne avec d'autres types:
Flotteurs:
>>> print myintfunction(2.2)
4.2
Nombres complexes:
>>> print myintfunction(5j)
(2+5j)
Décimales:
>>> import decimal
>>> myintfunction(decimal.Decimal('15'))
Decimal("17")
Même des objets complètement arbitraires qui peuvent ajouter des chiffres!
>>> class MyAdderClass(object):
... def __radd__(self, value):
... print 'got some value: ', value
... return 25
...
>>> m = MyAdderClass()
>>> print myintfunction(m)
got some value: 2
25
Donc, vous n'obtenez clairement rien en vérifiant le type. Et perdre beaucoup.
Depuis que vous avez modifié la question, il est maintenant clair que votre application appelle une routine en amont qui n’a de sens que pour ints.
Ceci étant, je pense toujours que vous devriez passer le paramètre tel que reçuà la fonction amont. La fonction amont le traitera correctement, par exemple en générant une erreur si besoin est. Je hautement doute quevotre La fonction qui traite les adresses IP se comportera de manière étrange si vous lui transmettez un float. Si vous pouvez nous donner le nom de la bibliothèque, nous pouvons le vérifier pour vous.
Mais ... Si la fonction en amont se comporte de manière incorrecte et tue des enfants si vous lui passez un flotteur (j'en doute fortement), alors appelez simplement int()
:
def myintfunction(value):
""" Please pass an integer """
return upstreamfunction(int(value))
Vous ne faites toujours pas de vérification de type, de sorte que vous obtenez le plus d'avantages de ne pas vérifier.
Si même après tout cela, vous voulez vraiment taper check, bien que cela réduise la lisibilité et les performances de votre application sans aucun avantage, utilisez une assert
pour le faire.
assert isinstance(...)
assert type() is xxxx
De cette façon, nous pouvons désactiver assert
s et supprimer ce <sarcasm>
feature </sarcasm>
du programme en l’appelant ainsi:
python -OO program.py
if type(n) is int
Ceci vérifie si n
est un int Python, et seulement un int. Il n'acceptera pas les sous-classes de int
.
La vérification de type ne correspond toutefois pas à la "méthode Python". Vous feriez mieux d'utiliser n
en tant qu'int, et s'il lève une exception, interceptez-la et agissez en conséquence.
Python supporte maintenant typage progressif via le module de typage et mypy . Le module typing
fait partie de la bibliothèque stdlib à partir de Python 3.5 et peut être téléchargé depuis PyPi si vous avez besoin de sauvegardes pour Python 2 ou une version précédente de Python 3. Vous pouvez installer mypy
en exécutant pip install mypy
à partir de la ligne de commande. .
En bref, si vous voulez vérifier qu'une fonction prend un int, un float et retourne une chaîne, vous annoterez votre fonction de la manière suivante:
def foo(param1: int, param2: float) -> str:
return "testing {0} {1}".format(param1, param2)
Si votre fichier s'appelle test.py
, vous pouvez ensuite vérifier une fois que vous avez installé mypy en exécutant mypy test.py
à partir de la ligne de commande.
Si vous utilisez une version antérieure de Python sans la prise en charge des annotations de fonctions, vous pouvez utiliser les commentaires de type pour obtenir le même effet:
def foo(param1, param2):
# type: (int, float) -> str
return "testing {0} {1}".format(param1, param2)
Vous utilisez la même commande mypy test.py
pour les fichiers Python 3 et mypy --py2 test.py
pour les fichiers Python 2.
Les interprétations Python ignorant complètement les annotations de type lors de l'exécution, elles n'imposent donc qu'une surcharge minime ou aucune surcharge. Le flux de travail habituel consiste à travailler sur votre code et à exécuter mypy régulièrement pour détecter les erreurs et les erreurs. Certains IDE, tels que PyCharm, comprendront les indications de type et pourront vous alerter des problèmes et des incohérences de type dans votre code lors de la modification directe.
Si, pour une raison quelconque, vous souhaitez que les types soient vérifiés au moment de l'exécution (vous devez peut-être valider de nombreuses entrées?), Vous devez suivre les conseils énumérés dans les autres réponses - par exemple. utilisez isinstance
, issubclass
, etc. Il existe également des bibliothèques telles que enforce qui tentent de vérifier le typage (en respectant vos annotations de type) au moment de l'exécution, bien que je ne sache pas dans quelle mesure elles sont prêtes pour la production au moment de la rédaction.
Pour plus d'informations et de détails, voir le site Web de mypy , le monypy FAQ et le PEP 484 .
Programmer en Python et effectuer des vérifications de frappe comme dans d’autres langues, c’est comme choisir un tournevis pour percer un clou. Il est plus élégant d'utiliser les fonctionnalités de gestion des exceptions de Python.
À partir d'une ligne de commande interactive, vous pouvez exécuter une instruction telle que:
int('sometext')
Cela va générer une erreur - ipython me dit:
<type 'exceptions.ValueError'>: invalid literal for int() with base 10: 'sometext'
Maintenant vous pouvez écrire du code comme:
try:
int(myvar) + 50
except ValueError:
print "Not a number"
Cela peut être personnalisé pour effectuer toutes les opérations requises ET pour intercepter les erreurs attendues. Cela semble un peu compliqué mais correspond à la syntaxe et aux idiomes de Python et donne un code très lisible (une fois que vous vous êtes familiarisé avec Python).
Ne tapez pas chèque. Le but de la frappe de canard est que vous ne devriez pas avoir à le faire. Par exemple, si quelqu'un faisait quelque chose comme ceci:
class MyInt(int):
# ... extra stuff ...
que diriez-vous:
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
bien sûr il y a la fonction standard isinstance (3, int) ...
Je serais tenté de faire quelque chose comme:
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)
Ainsi, lorsque vous l'utilisez, tout peut être transmis, mais vous ne stockez qu'un nombre entier valide.