145 votes

Python: la plupart des pythonic façon de vérifier si un objet est un nombre

Compte tenu de l'arbitraire d'un objet python, quelle est la meilleure façon de déterminer si c'est un nombre? Ici, is est défini comme acts like a number in certain circumstances.

Par exemple, supposons que vous écrivez un vecteur de classe. Si un autre vecteur, vous voulez trouver le produit scalaire. Si un scalaire, vous souhaitez mettre à l'échelle de l'ensemble du vecteur.

Vérifier si quelque chose est - int, float, long, bool est ennuyeux et ne couvre pas les objets définis par l'utilisateur qui pourraient agir comme des numéros. Mais, la vérification de la __mul__, par exemple, n'est pas assez bon, parce que la classe vector je viens de le décrire serait de définir __mul__, mais il ne serait pas le genre de numéro que je veux.

163voto

Steven Rumbalski Points 16838

Utiliser Number de la numbers module de test isinstance(n, Number) (disponible depuis la 2.6).

>>> from numbers import Number
... from decimal import Decimal
... from fractions import Fraction
... for n in [2, 2.0, Decimal('2.0'), complex(2,0), Fraction(2,1), '2']:
...     print '%15s %s' % (n.__repr__(), isinstance(n, Number))
              2 True
            2.0 True
 Decimal('2.0') True
         (2+0j) True
 Fraction(2, 1) True
            '2' False

C'est, bien sûr, contrairement à duck-typing. Si vous êtes plus préoccupé par la façon dont un objet des actes plutôt que de ce qu'il est, d'effectuer vos activités comme si vous disposez d'un nombre et de l'utilisation des exceptions à vous dire le contraire.

33voto

Alex Martelli Points 330805

Vous voulez vérifier si un objet

agit comme un certain nombre dans certaines circonstances

Si vous êtes à l'aide de Python 2.5 ou plus, la seule façon est de vérifier certains de ces "certaines circonstances" et de voir.

2.6 ou mieux, vous pouvez utiliser isinstance avec des chiffres.Nombre -- une classe de base abstraite (ABC) qui existe exactement pour ce but (beaucoup plus Abc existent dans l' collections module pour les diverses formes de collections ou de récipients, en commençant à nouveau par 2.6; et, aussi que dans ces versions, vous pouvez facilement ajouter vos propres classes de base abstraites si vous avez besoin).

De Bach à 2.5 et les versions antérieures, "peut être ajouté à l' 0 et n'est pas itératif" pourrait être une bonne définition dans certains cas. Mais, vous avez vraiment besoin de vous demander ce que c'est que vous vous demandez ce que vous voulez à considérer "un certain nombre" doit certainement être en mesure de le faire, et ce qu'elle doit être absolument incapable de le faire -- et vérifier.

Cela peut aussi être nécessaire en 2.6 ou version ultérieure, peut-être dans le but de faire vos propres enregistrements pour ajouter des types de soins à ce sujet n'est pas déjà être inscrit sur numbers.Numbers - si vous souhaitez exclure certains types qui prétendent qu'ils sont des numéros, mais vous ne pouvez pas gérer, qui prend encore plus de soin, comme l'Abc ont pas d' unregister [[par exemple, vous pourriez faire votre propre ABC WeirdNum et y inscrire toutes ces bizarre-pour-vous les types, puis vérifiez tout d'abord pour isinstance de celle-ci pour renflouer avant de procéder à la vérification de isinstance de la normale numbers.Number pour continuer avec succès.

BTW, si et quand vous en avez besoin pour vérifier si x peut ou ne peut pas faire quelque chose, vous pouvez généralement essayez quelque chose comme:

try: 0 + x
except TypeError: canadd=False
else: canadd=True

La présence d' __add__ soi vous dit rien d'utile, puisque e.g toutes les séquences ont pour objectif de concaténation avec d'autres séquences. Cette vérification est équivalente à la définition "un nombre est quelque chose de tel qu'une séquence de telles choses est valable un seul argument à la fonction builtin sum", par exemple. Complètement dingue de types (par exemple celles qui augmentent le "mauvais" exception lorsque additionnés à 0, comme, disons, un ZeroDivisionError ou ValueError &c) de propagation d'exception, mais c'est OK, permettre à l'utilisateur de connaître le plus tôt possible qu'un tel fou types sont tout simplement pas acceptable en bonne compagnie;-); mais, d'un "vecteur" c'est summable un scalaire (Python standard library n'en a pas, mais bien sûr, ils sont populaires en tant que tierce partie extensions) permettrait également de donner le mauvais résultat ici, donc (par exemple) cette vérification doit venir après le "n'est pas autorisé à être itératif" (par exemple, vérifier qu' iter(x) soulève TypeError, ou de la présence de méthode particulière, __iter__ -- si vous êtes en 2.5 ou version antérieure, et donc besoin de vos propres contrôles).

Un bref coup d'œil à l'une de ces complications peut être suffisant pour vous motiver à s'appuyer plutôt sur les classes de base abstraites dans la mesure du possible...;-).

17voto

Jochen Ritzel Points 42916

C'est un bon exemple où les exceptions à vraiment briller. Juste faire ce que vous pouvez faire avec les types numériques et attraper l' TypeError de tout le reste.

Mais évidemment, ce ne vérifie si une opération de travaux, pas de savoir si elle a du sens! La seule vraie solution pour cela est de ne jamais mélanger les types et de toujours savoir exactement ce typeclass vos valeurs appartiennent.

2voto

sth Points 91594

Probablement, il est préférable de simplement le faire dans l'autre sens: Vous vérifier si c'est un vecteur. Si elle l'est, vous faites un point produit et dans tous les autres cas, vous essayez de multiplication scalaire.

Vérifier le vecteur est facile, puisqu'il devrait de votre vecteur type de classe (ou par héritage). Vous pourriez tout aussi bien essayer d'abord de faire un produit scalaire, et si cela échoue (= il n'était pas vraiment un vecteur), avant de retomber à la multiplication scalaire.

0voto

katrielalex Points 40655

Pour l'hypothétique classe vector:

Supposons v est un vecteur, et nous sommes en multipliant par x. Si elle fait sens pour multiplier chaque composant de l' v par x, nous avons probablement dire que, donc essayez d'abord. Si non, peut-être que nous pouvons dot? Sinon c'est une erreur de type.

EDIT -- le code ci-dessous ne fonctionne pas, car 2*[0]==[0,0] , au lieu de lever une TypeError. Je le laisse car il a été dit.

def __mul__( self, x ):
    try:
        return [ comp * x for comp in self ]
    except TypeError:
        return [ x * y for x, y in itertools.zip_longest( self, x, fillvalue = 0 )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X