Какой самый питонный способ проверить, является ли объект числом? - PullRequest
98 голосов
/ 09 августа 2010

Для произвольного объекта python, как лучше определить, является ли он числом? Здесь is определяется как acts like a number in certain circumstances.

Например, скажем, вы пишете векторный класс. Если задан другой вектор, вы хотите найти скалярное произведение. Если дан скаляр, вы хотите масштабировать весь вектор.

Проверка, является ли что-то int, float, long, bool раздражает и не охватывает определенные пользователем объекты, которые могут действовать как числа. Но проверка на __mul__, например, недостаточно хороша, потому что класс векторов, который я только что описал, определил бы __mul__, но это не тот тип числа, который я хочу.

Ответы [ 12 ]

116 голосов
/ 09 августа 2010

Используйте Number из модуля numbers для проверки isinstance(n, Number) (доступно с версии 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

Это, конечно, противоречит типу утки.Если вас больше беспокоит то, как объект действует , а не то, чем является , выполняйте свои операции так, как будто у вас есть число, и используйте исключения, чтобы сказать вам обратное.

29 голосов
/ 09 августа 2010

Вы хотите проверить, действует ли какой-либо объект

в определенных обстоятельствах как число

Если вы используете Python 2.5 или более раннюю версию, единственным реальным способом являетсячтобы проверить некоторые из этих «определенных обстоятельств» и увидеть.

В версии 2.6 или выше, вы можете использовать isinstance с числами. Число - существующий абстрактный базовый класс (ABC)именно для этой цели (в модуле collections существует гораздо больше ABC для различных форм коллекций / контейнеров, снова начиная с 2.6; и, также, только в этих выпусках, вы можете легко добавлять свои собственные абстрактные базовые классы, если вам нужно).

Бах с 2.5 и более ранними версиями, «может быть добавлен к 0 и не повторяется» может быть хорошим определением в некоторых случаях.Но вы действительно должны спросить себя, что вы спрашиваете, что то, что вы хотите считать «числом», должно определенно быть способным делать , и что оно должно быть абсолютно неспособным сделать - и проверить.

Это также может потребоваться в версии 2.6 или более поздней, возможно, с целью создания собственных регистраций для добавления типов, которые вам не безразличны и которые еще не зарегистрированы numbers.Numbers - если вы хотите исключить некоторые типы, которые утверждают, что они числа, но вы просто не можете их обработать, это требует еще большей осторожности, так как у ABC нет unregister метода [[например, вымог бы создать свой собственный ABC WeirdNum и зарегистрировать там все такие странные для вас типы, затем сначала проверьте его на isinstance, чтобы выручить, прежде чем приступить к проверке isinstance нормального numbers.Number для успешного продолжения.

Кстати, если и когда вам нужно проверить, может ли x что-то сделать или нет, вам обычно нужно попробовать что-то вроде:

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

Наличие __add__ само по себеговорит вам ничего не использоватьнапример, все последовательности имеют его с целью объединения с другими последовательностями.Эта проверка эквивалентна определению «число - это нечто такое, что последовательность таких вещей является допустимым единственным аргументом встроенной функции sum», например.Совершенно странные типы (например, те, которые выдают «неправильное» исключение при суммировании до 0, например, например, ZeroDivisionError или ValueError & c) будут распространять исключения, но это нормально, пусть пользователь знает как можно скорее, что такие сумасшедшие типыпросто не приемлемы в хорошей компании ;-);но «вектор», который суммируется со скаляром (стандартная библиотека Python не имеет такового, но, конечно, они популярны как сторонние расширения), также дал бы неправильный результат, поэтому (например) эта проверка должна прийти после «не разрешено быть повторяемым» (например, проверьте, что iter(x) повышает TypeError, или на наличие специального метода __iter__ - если вы используете 2.5 или более раннюю версию и, таким образом,нужны ваши собственные проверки).

Краткого обзора таких сложностей может быть достаточно, чтобы мотивировать вас вместо этого полагаться на абстрактные базовые классы, когда это возможно ...; -).

16 голосов
/ 09 августа 2010

Это хороший пример, где исключения действительно сияют. Просто сделайте то, что вы сделали бы с числовыми типами, и поймайте TypeError от всего остального.

Но очевидно, что это только проверяет, работает ли операция , а не имеет ли смысл ! Единственное реальное решение для этого - никогда не смешивать типы и всегда точно знать, к какому классу типов принадлежат ваши значения.

4 голосов
/ 07 июня 2017

Умножьте объект на ноль.Любое число, умноженное на ноль, равно нулю.Любой другой результат означает, что объект не является числом (включая исключения)

def isNumber(x):
    try:
        return 0 == x*0
    except:
        return False

Использование isNumber таким образом даст следующий вывод:

class A: pass 

def foo(): return 1

for x in [1,1.4, A(), range(10), foo, foo()]:
    answer = isNumber(x)
    print '{answer} == isNumber({x})'.format(**locals())

Вывод:

True == isNumber(1)
True == isNumber(1.4)
False == isNumber(<__main__.A instance at 0x7ff52c15d878>)
False == isNumber([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
False == isNumber(<function foo at 0x7ff52c121488>)
True == isNumber(1)

Возможно, в мире существуют нечисловые объекты, которые определяют __mul__, чтобы возвращать ноль при умножении на ноль, но это крайнее исключение.Это решение должно охватывать весь обычный и нормальный код, который вы генерируете / приветствуете.

3 голосов
/ 20 января 2016

Чтобы перефразировать ваш вопрос, вы пытаетесь определить, является ли что-то коллекцией или единственным значением. Пытаясь сравнить, является ли что-то вектором или числом, сравнивает яблоки с апельсинами - у меня может быть вектор строк или чисел, и у меня может быть одна строка или одно число. Вас интересует, сколько у вас (1 или более) , а не тип, который у вас есть на самом деле.

Мое решение для этой проблемы состоит в том, чтобы проверить, является ли ввод одним значением или коллекцией, проверив наличие __len__. Например:

def do_mult(foo, a_vector):
    if hasattr(foo, '__len__'):
        return sum([a*b for a,b in zip(foo, a_vector)])
    else:
        return [foo*b for b in a_vector]

Или, для подхода утки, вы можете попытаться повторить сначала foo:

def do_mult(foo, a_vector):
    try:
        return sum([a*b for a,b in zip(foo, a_vector)])
    except TypeError:
        return [foo*b for b in a_vector]

В конечном счете, проще проверить, является ли что-то векторным, чем проверить, является ли что-то скалярным. Если у вас есть значения различного типа (например, строковые, числовые и т. Д.), Тогда логике вашей программы может потребоваться некоторая работа - как вы в итоге попытались умножить строку на числовой вектор в первую очередь? 1013 *

2 голосов
/ 20 сентября 2017

Чтобы суммировать / оценить существующие методы:

Candidate    | type                      | delnan | mat | shrewmouse | ant6n
-------------------------------------------------------------------------
0            | <type 'int'>              |      1 |   1 |          1 |     1
0.0          | <type 'float'>            |      1 |   1 |          1 |     1
0j           | <type 'complex'>          |      1 |   1 |          1 |     0
Decimal('0') | <class 'decimal.Decimal'> |      1 |   0 |          1 |     1
True         | <type 'bool'>             |      1 |   1 |          1 |     1
False        | <type 'bool'>             |      1 |   1 |          1 |     1
''           | <type 'str'>              |      0 |   0 |          0 |     0
None         | <type 'NoneType'>         |      0 |   0 |          0 |     0
'0'          | <type 'str'>              |      0 |   0 |          0 |     1
'1'          | <type 'str'>              |      0 |   0 |          0 |     1
[]           | <type 'list'>             |      0 |   0 |          0 |     0
[1]          | <type 'list'>             |      0 |   0 |          0 |     0
[1, 2]       | <type 'list'>             |      0 |   0 |          0 |     0
(1,)         | <type 'tuple'>            |      0 |   0 |          0 |     0
(1, 2)       | <type 'tuple'>            |      0 |   0 |          0 |     0

(я пришел сюда этот вопрос )

код

#!/usr/bin/env python

"""Check if a variable is a number."""

import decimal


def delnan_is_number(candidate):
    import numbers
    return isinstance(candidate, numbers.Number)


def mat_is_number(candidate):
    return isinstance(candidate, (int, long, float, complex))


def shrewmouse_is_number(candidate):
    try:
        return 0 == candidate * 0
    except:
        return False


def ant6n_is_number(candidate):
    try:
        float(candidate)
        return True
    except:
        return False

# Test
candidates = (0, 0.0, 0j, decimal.Decimal(0),
              True, False, '', None, '0', '1', [], [1], [1, 2], (1, ), (1, 2))

methods = [delnan_is_number, mat_is_number, shrewmouse_is_number, ant6n_is_number]

print("Candidate    | type                      | delnan | mat | shrewmouse | ant6n")
print("-------------------------------------------------------------------------")
for candidate in candidates:
    results = [m(candidate) for m in methods]
    print("{:<12} | {:<25} | {:>6} | {:>3} | {:>10} | {:>5}"
          .format(repr(candidate), type(candidate), *results))
2 голосов
/ 09 августа 2010

Возможно, лучше просто сделать это наоборот: вы проверяете, является ли это вектором.Если это так, вы делаете скалярное произведение, а во всех других случаях вы пытаетесь скалярное умножение.

Проверка на вектор проста, так как это должен быть тип вашего векторного класса (или унаследованный от него).Вы также можете сначала попытаться создать точечное произведение, а если это не удастся (= на самом деле это был не вектор), то вернуться к скалярному умножению.

1 голос
/ 05 августа 2014

Просто добавлю.Возможно, мы можем использовать комбинацию isinstance и isdigit следующим образом, чтобы определить, является ли значение числом (int, float и т. Д.)

, если isinstance (num1, int) или isinstance (num1, float) или num1.isdigit ():

0 голосов
/ 28 марта 2019

Если вы хотите вызывать разные методы в зависимости от типа (ов) аргумента, посмотрите на multipledispatch.

Например, скажем, вы пишете векторный класс,Если задан другой вектор, вы хотите найти скалярное произведение.Если дан скаляр, вы хотите масштабировать весь вектор.

from multipledispatch import dispatch

class Vector(list):

    @dispatch(object)
    def __mul__(self, scalar):
        return Vector( x*scalar for x in self)

    @dispatch(list)
    def __mul__(self, other):
        return sum(x*y for x,y in zip(self, other))


>>> Vector([1,2,3]) * Vector([2,4,5])   # Vector time Vector is dot product
25
>>> Vector([1,2,3]) * 2                 # Vector times scalar is scaling
[2, 4, 6]

К сожалению, (насколько мне известно) мы не можем написать @dispatch(Vector), так как мы все еще определяем тип Vector, так что имя типа еще не определено.Вместо этого я использую базовый тип list, который позволяет вам даже найти скалярное произведение Vector и list.

0 голосов
/ 01 сентября 2017

Вы можете использовать функцию isdigit ().

>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False
...