python: як визначити, чи є змінною масив чи скаляр


283

У мене є функція, яка бере аргумент 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?

Дякую


3
Ви пробували тестування на це type?
Сукрит Калра

Відповіді:


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

Щоб підтримувати будь-який тип послідовності, поставте прапорець collections.Sequenceзамість list.

Примітка : isinstanceтакож підтримує набір класів, type(x) in (..., ...)слід уникати перевірки і не потрібна.

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


3
спасибі, я не уявляв, listяк
перетворювати

3
Хоча це чудова відповідь, collections.Sequenceчи є ABC і для рядка, тому це слід враховувати. Я використовую щось подібне 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. Оскільки хтось часто використовує нуд, я рекомендую дуже пітонічний тест:

if hasattr(N, "__len__")

12
рядки мають __len__атрибут (тому, мабуть, технічно не скалярний тип)
xofer

20
if hasattr(N, '__len__') and (not isinstance(N, str))правильно би враховував рядки.
Тукідід411

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таким же чином.


Це відповідь, яка найбільше відповідала моїм потребам. Я також додав набір теж. Тому що я не хочу бути стійким проти диктів. isinstance(P, (list, tuple, set, np.ndarray))
Сантьяго

32

Чи є еквівалент isscalar () у numpy? Так.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True

6
Було б краще і приклад: >>> np.isscalar('abcd')повертається True.
Сиртіс майор

Дякую! це набагато більш загальний приклад, ніж будь-який із перерахованих вище, і його слід віддати перевагу. Це також пряма відповідь на питання ОП.
Крістобаль Сіфон

1
Приємно. Незважаючи на те, що одна гама є, що високий розмір (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), оскільки перший також правильно поверне справжнє для масивів 0d." Таким чином, надійною альтернативою, сумісною вперед, numpy.isscalar()буде тривіально numpy.ndim()def is_scalar(obj): return np.ndim(obj) == 0
Сесіль Керрі,

Насправді цього не слід заохочувати, оскільки np.isscalarце заплутано. Офіційний документ запропонував використовувати np.array.ndimскрізь, тобто np.isscalar(np.array(12))помилковий, хоча його слід вважати скалярним, оскільки 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 = [] >>> типу (p) в (список) Traceback (останній останній виклик): Файл "<stdin>" , рядок 1, у <модулі>
Олег Гриб

@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

Не потрібно нічого імпортувати.


3

Ось найкращий підхід, який я знайшов: Перевірка існування __len__та __getitem__.

Ви можете запитати, чому? Причини включають:

  1. Популярний метод isinstance(obj, abc.Sequence)не працює на деяких об'єктах, включаючи тензор PyTorch, оскільки вони не реалізуються __contains__.
  2. На жаль, немає нічого в collections.abc Пайтона , що перевіряє тільки __len__і __getitem__які я відчуваю мінімальні методи масивів як об'єкти.
  3. Він працює у списку, кортежі, ndarray, тензорі тощо.

Тож без зайвої приналежності:

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

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

Я також здивований, що жоден з них, схоже, не має справу з генераторами.
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' не визначено
thang

1
Це правда. Я використовував numpy size, не помічаючи цього. Вам потрібно: від нумерного розміру імпорту
Mathieu Villion

2
np.size(5)і np.size([5])обидва == 1, тому це неправильно розрізняє тип (тобто ідентифікувати скаляр), що, на мою думку, є ціллю.
Майкл

Це цікаве зауваження. Оригінальне запитання стосується величини, яка є функцією Matlab. У Matlab абсолютно немає різниці між скаляром та масивом розміром 1, може це вектор чи N-dim масив. ІМХО, це плюс для Matlab.
Матьє Вільйон

0

preds_test [0] має форму (128,128,1) Дозволяє перевірити тип даних за допомогою функції isinstance (). 1-й аргумент - це дані, 2-й аргумент - тип даних isin substance (preds_test [0], np.ndarray) дає вихідний результат як True. Це означає, що 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')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.