Какой самый питонический способ проверить, является ли объект числом?

Учитывая произвольный объект Python, как лучше всего определить, является ли это числом? Здесь равно определяется как в определенных обстоятельствах действует как число .

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

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

104
задан Martin Thoma 21 September 2017 в 11:36
поделиться

5 ответов

Используйте 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

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

125
ответ дан 24 November 2019 в 04:10
поделиться

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

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

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

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

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

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

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

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

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

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

30
ответ дан 24 November 2019 в 04:10
поделиться

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

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

17
ответ дан 24 November 2019 в 04:10
поделиться

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

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

2
ответ дан 24 November 2019 в 04:10
поделиться

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

Предположим, 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
ответ дан 24 November 2019 в 04:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: