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



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



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



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

578   11  

11 ответов:

использовать 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

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

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

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

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

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

Бах до 2,5 и ранее, "можно добавить к 0 и не повторяется" может быть хорошим определением в некоторых случаях. Но, вы действительно должны спросить себя, что это такое, что вы спрашиваете, что вы хотите рассмотреть "число", безусловно, должны быть в состоянии do, а какой она должна быть абсолютно можете - и проверить.

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

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

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

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

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

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

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

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

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__ чтобы вернуть ноль при умножении на ноль, но это крайнее исключение. Это решение должно охватывать все нормальный и вменяемого код, который вы создаете / encouter.

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

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

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

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

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

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

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

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))

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

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

EDIT -- приведенный ниже код не работает, потому что 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 )

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

float(x)

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

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

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

Comments

    Ничего не найдено.