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

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

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

130
Claudiu 9 Авг 2010 в 19:04

12 ответов

Лучший ответ

Используйте 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(f'{n!r:>14} {isinstance(n, Number)}')
              2 True
            2.0 True
 Decimal('2.0') True
         (2+0j) True
 Fraction(2, 1) True
            '2' False

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

129
Steven Rumbalski 27 Янв 2020 в 16:17

Для гипотетического векторного класса:

Предположим, что v является вектором, и мы умножаем его на x. Если имеет смысл умножить каждый компонент v на x, мы, вероятно, имели в виду это, поэтому попробуйте сначала. Если нет, может быть, мы можем расставить все точки? В противном случае это ошибка типа.

РЕДАКТИРОВАТЬ - приведенный ниже код не работает, потому что 2*[0]==[0,0] вместо повышения TypeError. Я оставляю это, потому что это было прокомментировано.

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 )
0
Katriel 9 Авг 2010 в 20:47

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

>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False
-1
rsy 31 Авг 2017 в 21:01

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

float(x)

Это должно отклонить случаи, когда x не может быть преобразован в число; но могут также отклонять другие виды числовых структур, которые могут быть действительными, например, комплексные числа.

0
Ant6n 6 Дек 2014 в 03:09

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

def isNumber(x):
    try:
        return bool(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__ для возврата нуля при умножении на ноль, но это крайнее исключение. Это решение должно охватывать весь нормальный и нормальный код, который вы генерируете / приветствуете.

Пример numpy.array:

import numpy as np

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

x = np.array([0,1])

answer = isNumber(x)
print('{answer} == isNumber({x})'.format(**locals()))

Выход:

False == isNumber([0 1])
4
shrewmouse 16 Авг 2019 в 19:11

Перефразируя ваш вопрос, вы пытаетесь определить, является ли что-то коллекцией или единственным значением. Пытаясь сравнить, является ли что-то вектором или числом, сравнивает яблоки с апельсинами - у меня может быть вектор строк или чисел, и у меня может быть одна строка или одно число. Вас интересует, сколько у вас (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]

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

3
Gordon Bean 20 Янв 2016 в 17:00

Подводя итог / оценить существующие методы:

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))
3
Martin Thoma 20 Сен 2017 в 14:19

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

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

2
sth 9 Авг 2010 в 15:13

Если вы хотите вызывать разные методы в зависимости от типа (ов) аргументов, посмотрите 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
AJNeufeld 27 Мар 2019 в 22:14

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

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

17
Jochen Ritzel 9 Авг 2010 в 15:14

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

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

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

В версии 2.6 или выше вы можете использовать isinstance с numbers.Number - абстрактный базовый класс (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 его нет, но, конечно, они популярны как сторонние расширения), здесь также будет неправильный результат, поэтому (например) эта проверка должна выполняться < em> после "не допускается повторение" (например, проверьте, что iter(x) повышает TypeError, или на наличие специального метода __iter__ - если вы ' re в версии 2.5 или более ранней и, следовательно, нужны ваши собственные проверки).

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

31
Alex Martelli 9 Авг 2010 в 17:19

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

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

1
shadab.tughlaq 5 Авг 2014 в 13:31