Python: как определить, является ли переменная массивом или скаляром

284

У меня есть функция, которая принимает аргумент NBins. Я хочу сделать вызов этой функции с помощью скаляра 50или массива [0, 10, 20, 30]. Как я могу определить в функции, какова длина NBins? или сказал иначе, если это скаляр или вектор?

Я попробовал это:

>>> N=[2,3,5]
>>> P = 5
>>> len(N)
3
>>> len(P)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
>>> 

Как вы видите, я не могу обратиться lenк P, так как это не массив .... Есть ли что - то подобное isarrayили isscalarв Python?

Спасибо

otmezger
источник
3
Вы пробовали проверить это type?
Сукрит Калра

Ответы:

391
>>> isinstance([0, 10, 20, 30], list)
True
>>> isinstance(50, list)
False

Чтобы поддерживать любой тип последовательности, отметьте collections.Sequenceвместо list.

примечание : isinstanceтакже поддерживает кортеж классов, type(x) in (..., ...)следует избегать проверки и не требуется.

Вы также можете проверить not isinstance(x, (str, unicode))

jamylak
источник
3
спасибо, я не представлял, что инвертирую, listчтобы получить ложные значения для скаляров ... спасибо
otmezger
3
Хотя это отличный ответ, collections.Sequenceон также является азбукой для строки, так что это следует учитывать. Я использую что-то вроде if type(x) is not str and isinstance(x, collections.Sequence):. Это не здорово, но это надежно.
bbenne10
2
@ bbenne10 конечно, но избегайте type, а также проверяйте not isinstance(x, (str, unicode))Python 2
jamylak
Почему вы сказали, что «проверки типа (x) в (..., ...) следует избегать, и в этом нет необходимости»? Если вы так говорите, было бы очень любезно объяснить, почему, может быть, я не единственный, кто задается вопросом, почему этого следует избегать.
Оливье Понс
119

Предыдущие ответы предполагают, что массив представляет собой стандартный список Python. Как человек, который часто использует numpy, я бы порекомендовал очень питонический тест:

if hasattr(N, "__len__")
jpaddison3
источник
12
Строки имеют __len__атрибут (так что, я думаю, технически это не скалярный тип)
xofer
20
if hasattr(N, '__len__') and (not isinstance(N, str))будет правильно учитывать строки.
Thucydides411
1
Также приходится диктовать на Python 3
Бруно Энрике
44

Комбинируя ответы @jamylak и @ jpaddison3 вместе, если вам нужно быть устойчивым к массивным массивам в качестве входных данных и обрабатывать их так же, как списки, вы должны использовать

import numpy as np
isinstance(P, (list, tuple, np.ndarray))

Это устойчиво к подклассам списков, кортежей и массивов.

И если вы хотите быть устойчивым ко всем другим подклассам последовательности (не только к списку и кортежу), используйте

import collections
import numpy as np
isinstance(P, (collections.Sequence, np.ndarray))

Почему вы должны так поступать, isinstanceа не сравнивать type(P)с целевым значением? Вот пример, где мы создаем и изучаем поведение NewListтривиального подкласса списка.

>>> class NewList(list):
...     isThisAList = '???'
... 
>>> x = NewList([0,1])
>>> y = list([0,1])
>>> print x
[0, 1]
>>> print y
[0, 1]
>>> x==y
True
>>> type(x)
<class '__main__.NewList'>
>>> type(x) is list
False
>>> type(y) is list
True
>>> type(x).__name__
'NewList'
>>> isinstance(x, list)
True

Несмотря на xи yсравнение , как равные, обработка их typeможет привести к различному поведению. Однако, так xкак это экземпляр подкласса list, использование isinstance(x,list)дает желаемое поведение и обрабатывает xи yтаким же образом.

scottclowe
источник
Это ответ, который больше всего соответствует моим потребностям. Я только что добавил сет тоже. Потому что я не хочу быть сильным против диктов. isinstance(P, (list, tuple, set, np.ndarray))
Сантьяго
32

Есть ли в numpy эквивалент isscalar ()? Да.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True
jmhl
источник
6
Было бы лучше и пример: >>> np.isscalar('abcd')возврат True.
Syrtis Major
Спасибо! это гораздо более общий пример, чем любой из вышеперечисленных и должен быть предпочтительным. Это также прямой ответ на вопрос ОП.
Кристобаль Сифон
1
Ницца. Хотя есть одна ошибка, что isscalar (None) возвращает False. Numpy реализует это какreturn (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))
Shital Shah
5
Нет, к сожалению. Эта numpy.isscalar()функция страдает рядом несовместимых недостатков дизайна и, вероятно, будет устарела в некоторых будущих версиях. Перефразируя официальную документацию : «Почти во всех случаях np.ndim(x) == 0следует использовать вместо np.isscaler(x), так как первый также будет правильно возвращать true для 0d массивов». numpy.isscalar()Таким образом, надежной альтернативой, совместимой с прямым вариантом, было бы тривиально завернуть numpy.ndim(): например,def is_scalar(obj): return np.ndim(obj) == 0
Сесил Карри
На самом деле это не должно быть проголосовано, потому что np.isscalarэто сбивает с толку. Официальный документ предложил использовать np.array.ndimвезде, то np.isscalar(np.array(12))есть False, тогда как его следует считать скалярным, поскольку np.array(12).ndimравен 0.
knh190
17

Хотя подход @ jamylak лучше, вот альтернативный подход

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True
Сукрит Калра
источник
2
Было бы замечательно, если бы человек, который отклонил ответ, тоже объяснил причину.
Сукрит Калра
на самом деле я проголосовал, но потом понял, что он не работает в 2.7: >>> p = [] >>> type (p) в (списке) Traceback (последний вызов был последним): Файл "<stdin>" , строка 1, в <module>
Олег Грыб,
@OlegGryb: попробуй type(p) in (list, ).
Сукрит Калра
ах, это кортеж справа, а не список, понял, спасибо, и теперь это работает. Сожалею, я не могу 2 раза поднять голос - пока лучшее решение :)
Олег Гриб
3

Другой альтернативный подход (использование свойства имени класса ):

N = [2,3,5]
P = 5

type(N).__name__ == 'list'
True

type(P).__name__ == 'int'
True

type(N).__name__ in ('list', 'tuple')
True

Не нужно ничего импортировать.

Marek
источник
3

Вот лучший подход, который я нашел: Проверьте наличие __len__и __getitem__.

Вы можете спросить почему? Причины включают в себя:

  1. Популярный метод isinstance(obj, abc.Sequence)не работает на некоторых объектах, включая Tensor PyTorch, потому что они не реализуют __contains__.
  2. К сожалению, в Python collection.abc нет ничего, что проверяло бы только __len__и __getitem__которое я считаю минимальными методами для объектов, подобных массиву.
  3. Работает с list, tuple, ndarray, Tensor и т. Д.

Так что без лишних слов:

def is_array_like(obj, string_is_array=False, tuple_is_array=True):
    result = hasattr(obj, "__len__") and hasattr(obj, '__getitem__') 
    if result and not string_is_array and isinstance(obj, (str, abc.ByteString)):
        result = False
    if result and not tuple_is_array and isinstance(obj, tuple):
        result = False
    return result

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

Шиталь шах
источник
2
>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False
suhailvs
источник
2

Вы можете проверить тип данных переменной.

N = [2,3,5]
P = 5
type(P)

Он выдаст вас как тип данных P.

<type 'int'>

Так что вы можете различить, что это целое число или массив.

уннати пати
источник
2

Я удивлен, что такой основной вопрос, кажется, не имеет немедленного ответа в Python. Мне кажется, что почти во всех предлагаемых ответах используется какая-то проверка типов, которая обычно не рекомендуется в python, и кажется, что они ограничены конкретным случаем (они терпят неудачу с различными числовыми типами или общими итеративными объектами, которые не являются кортежами или списками).

Для меня лучше всего импортировать numpy и использовать array.size, например:

>>> a=1
>>> np.array(a)
Out[1]: array(1)

>>> np.array(a).size
Out[2]: 1

>>> np.array([1,2]).size
Out[3]: 2

>>> np.array('125')
Out[4]: 1

Обратите внимание также:

>>> len(np.array([1,2]))

Out[5]: 2

но:

>>> len(np.array(a))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-40-f5055b93f729> in <module>()
----> 1 len(np.array(a))

TypeError: len() of unsized object
Vincenzooo
источник
Я также удивлен, что ни один из них, кажется, также не имеет дело с генераторами.
RhysC
2

Просто используйте sizeвместо len!

>>> from numpy import size
>>> N = [2, 3, 5]
>>> size(N)
3
>>> N = array([2, 3, 5])
>>> size(N)
3
>>> P = 5
>>> size(P)
1
Матье Виллион
источник
2
NameError: имя 'size' не определено
спасибо
1
Это правда. Я использовал маленький размер, не замечая этого. Вам нужно: из большого размера импорта
Матье Виллион
2
np.size(5)и np.size([5])оба == 1, так что это не правильно различает тип (то есть, идентифицирует скаляр), который я считаю целью.
Майкл
Это интересное замечание. Оригинальный вопрос относится к isscalar, который является функцией Matlab. В Matlab нет абсолютно никакой разницы между скаляром и массивом размера 1, может ли это быть вектор или массив N-dim. ИМХО, это плюс для Matlab.
Матье Вильон
0

preds_test [0] имеет форму (128,128,1). Давайте проверим тип данных с помощью функции isinstance (). isinstance принимает 2 аргумента. 1-й аргумент - это данные. 2-й аргумент - это тип данных isinstance (preds_test [0], np.ndarray). Это означает, что preds_test [0] является массивом.

Сумант Минан
источник
0

Чтобы ответить на вопрос в заголовке, прямой способ определить, является ли переменная скалярной, - попытаться преобразовать ее в число с плавающей точкой. Если вы получаете TypeError, это не так.

N = [1, 2, 3]
try:
    float(N)
except TypeError:
    print('it is not a scalar')
else:
    print('it is a scalar')
шайба
источник