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

115

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

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

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

Клаудиу
источник

Ответы:

136

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

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

Стивен Румбальский
источник
3
Делая умные вещи, а не утка вещей, является предпочтительной , когда вы умножение вектора на X. В этом случае , если вы хотите , чтобы делать разные вещи , основываясь на том, что X является . (Это может действовать как нечто умножающееся, но результат может быть бессмысленным.)
Евгений Сергеев
3
этот ответ дает сказать, что True - это число ... что, вероятно, не всегда то, что вы хотите. Для исключения логических значений (думаю, проверка fe) я бы сказалisinstance(value, Number) and type(value) != bool
Йо Людке
32

Вы хотите проверить, не

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

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

В версии 2.6 или выше вы можете использовать isinstancewith 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, например, например, a ZeroDivisionErrorили ValueError& c) будут распространять исключение, но это нормально, пусть пользователь узнает как можно скорее, что такие сумасшедшие типы просто неприемлемы в хорошем Компания;-); но «вектор», суммируемый в скаляр (в стандартной библиотеке Python его нет, но, конечно, они популярны как сторонние расширения), здесь также будет неправильный результат, поэтому (например,вариант «не допускается повторение» (например, проверка, которая iter(x)вызывает повышение TypeError, или наличие специального метода __iter__- если вы используете версию 2.5 или более раннюю и, следовательно, нуждаетесь в собственных проверках).

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

Алекс Мартелли
источник
Но в модуле чисел есть ABC для числа. Вот что утверждают документы: «Модуль чисел (PEP 3141) определяет иерархию числовых абстрактных базовых классов, которые постепенно определяют больше операций».
Стивен Румбальский, 09
17

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

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

Йохен Ритцель
источник
1
+1 за Duck Typing: неважно, к какому типу принадлежат мои данные, просто могу ли я делать с ними то, что хочу.
systempuntoout 09
12
Это был традиционный подход, но азбука была введена в значительной степени для того, чтобы уйти от чистого утиного набора текста и сместиться на некоторое расстояние в сторону мира, который isinstanceдействительно может быть полезен во многих случаях (== «проверьте, имеет ли смысл», а также формальную применимость операций). Трудный переход для тех, кто давно занимается только Python, но очень важная тонкая тенденция в философии Python, игнорировать которую было бы серьезной ошибкой.
Alex Martelli
@Alex: Верно, и я люблю классы типов (в основном collections.Sequenceи друзей). Но, черт возьми, таких классов для чисел, векторов или любых других математических объектов не существует.
Йохен Ритцель, 09
1
Ничего против утиного набора текста. Я бы так и поступил. Но есть абстрактный базовый класс для чисел: numbers.Number.
Стивен Румбальский,
4

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

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])
землеройка
источник
5
True * 0 == 0
эндолиты
4
Ваша функция будет неправильно сказать , что булевы являются цифры
эндолиты
1
@endolith, логические значения действуют точно так же, как числа. Истина всегда == 1 и Ложь всегда == 0. Это именно то, о чем спрашивал спрашивающий: «Здесь« это »определяется как« действует как число в определенных обстоятельствах ».
shrewmouse
1
@endolith, на самом деле логические числа - это числа. Логическое значение является производным от, intпоэтому моя функция будет правильно говорить, что логические значения - это числа.
shrewmouse
1
@NicolasAbril, преобразуйте 0 * x == 0 в логическое значение внутри isNumber.
shrewmouse
3

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

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

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))
Мартин Тома
источник
ТОДО для себя:, float('nan'), 'nan', '123.45', '42', '42a', '0x8', '0xa'добавлюmath.isnan
Мартин Тома
2

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

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

STH
источник
1

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

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

shadab.tughlaq
источник
0

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

Предположим 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 )
Катриэль
источник
if x- вектор, тогда [comp * x for comp in self]будет xполучен внешний продукт v. Это тензор 2-го ранга, а не скаляр.
aaronasterling
замените «не скаляр» на «не вектор». по крайней мере, не в исходном векторном пространстве.
aaronasterling
Хех, на самом деле мы оба ошибаемся. Вы предполагаете , что comp*xбудет масштабироваться xпутем comp, я предполагал , что это поднимет TypeError. К сожалению, на самом деле он будет сцепляться xсо своим compвременем. К сожалению.
Катриэль
Мех. если xэто вектор, то у него должен быть __rmul__метод ( __rmul__ = __mul__), чтобы он comp * xмасштабировался так xже, x * compкак и предполагалось.
aaronasterling
0

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

float(x)

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

Ant6n
источник
0

Если вы хотите вызывать разные методы в зависимости от типа (ов) аргумента, посмотрите 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, который позволяет вам даже найти точечное произведение a Vectorи a list.

AJNeufeld
источник
0

Короткий и простой способ:

obj = 12345
print(isinstance(obj,int))

Вывод :

True

Если объект является строкой, будет возвращено 'False':

obj = 'some string'
print(isinstance(obj,int))

Вывод :

False
Шекхар
источник
0

У вас есть элемент данных, скажем, rec_dayчто при записи в файл будет float. Но во время обработки программы это может быть либо float, intлибо strтип ( strиспользуется при инициализации новой записи и содержит фиктивное значение флага).

Затем вы можете проверить, есть ли у вас номер с этим

                type(rec_day) != str 

Я структурировал программу на Python таким образом и просто вставил «патч обслуживания», используя это как числовую проверку. Это питонический путь? Скорее всего, нет, потому что раньше я программировал на КОБОЛЕ.

CopyPasteIt
источник
-1

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

>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False
Rsy
источник
«01234» - это не число, это строка символов.
Алексей