python: jak rozpoznać, czy zmienna jest tablicą czy skalarem

283

Mam funkcję, która przyjmuje argument NBins. Chcę zadzwonić do tej funkcji za pomocą skalara50 lub tablicy [0, 10, 20, 30]. Jak mogę określić w ramach funkcji, jaka jest długość NBins? lub powiedział inaczej, jeśli jest to skalar lub wektor?

Próbowałem tego:

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

Jak widać, nie mogą ubiegać lensię P, ponieważ nie jest tablicą .... Czy istnieje coś takiego jakisarray lub isscalarw Pythonie?

dzięki

otmezger
źródło
3
Próbowałeś już na to przetestować type?
Sukrit Kalra

Odpowiedzi:

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

Aby obsłużyć dowolny typ sekwencji, zaznacz collections.Sequencezamiast list.

Uwaga : isinstanceobsługuje również krotkę klas, type(x) in (..., ...)należy unikać sprawdzania i nie jest konieczne.

Możesz także chcieć sprawdzić not isinstance(x, (str, unicode))

jamylak
źródło
3
dzięki, nie wyobrażałem sobie odwrócenia, listby uzyskać fałsz dla skalarów ... dzięki
otmezger
3
Chociaż jest to świetna odpowiedź, collections.Sequencejest to również ABC dla ciągu znaków, więc należy to wziąć pod uwagę. Używam czegoś takiego if type(x) is not str and isinstance(x, collections.Sequence):. To nie jest świetne, ale jest niezawodne.
bbenne10,
2
@ bbenne10 na pewno, ale unikaj type, a także sprawdź not isinstance(x, (str, unicode))Python 2
jamylak
Dlaczego powiedziałeś, że „należy unikać wpisywania (x) w (..., ...) i jest to niepotrzebne”? Jeśli tak mówisz, to byłoby bardzo miłe, aby wyjaśnić, dlaczego, być może nie jestem jedynym, który zastanawia się, dlaczego należy tego unikać.
Olivier Pons
119

Poprzednie odpowiedzi zakładają, że tablica jest standardową listą Pythona. Jako ktoś, kto często używa numpy, polecam bardzo pythonowy test:

if hasattr(N, "__len__")
jpaddison3
źródło
12
ciągi mają __len__atrybut (tak sądzę, technicznie nie jest to typ skalarny)
xofer
20
if hasattr(N, '__len__') and (not isinstance(N, str))poprawnie uwzględniłoby łańcuchy.
Thucydides411,
1
Weź również pod uwagę dyktat na Pythonie 3
Bruno Henrique
44

Łącząc odpowiedzi @jamylak i @ jpaddison3 razem, jeśli chcesz być odporny na tablice numpy jako dane wejściowe i obsługiwać je w taki sam sposób jak listy, powinieneś użyć

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

Jest to odporne na podklasy tablic list, krotek i tablic numpy.

A jeśli chcesz być odporny na wszystkie inne podklasy sekwencji (nie tylko listę i krotkę), użyj

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

Dlaczego warto robić to w ten sposób, isinstancea nie porównywać type(P)z wartością docelową? Oto przykład, w którym tworzymy i badamy zachowanie NewListtrywialnej podklasy listy.

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

Pomimo xi yporównywanie jako równe, obchodzenie się z nimi typespowodowałoby inne zachowanie. Jednakże, ponieważ xjest to przykład podklasy list, stosując isinstance(x,list)daje pożądane zachowanie i traktuje xi yw ten sam sposób.

scottclowe
źródło
To odpowiedź najbardziej odpowiada moim potrzebom. Właśnie dodałem zestaw. Ponieważ nie chcę być odporny na dyktanda. isinstance(P, (list, tuple, set, np.ndarray))
Santiago,
32

Czy istnieje odpowiednik isscalar () w numpy? Tak.

>>> np.isscalar(3.1)
True
>>> np.isscalar([3.1])
False
>>> np.isscalar(False)
True
jmhl
źródło
6
Byłoby lepiej i przykład: >>> np.isscalar('abcd')zwraca True.
Syrtis Major
dzięki! jest to znacznie bardziej ogólny przykład niż którykolwiek z powyższych i powinien być preferowany. To także bezpośrednia odpowiedź na pytanie PO.
Cristóbal Sifón
1
Miły. Chociaż jedną z nich jest to, że isscalar (None) zwraca False. Numpy implementuje to jakoreturn (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))
Shital Shah
5
Niestety niestety Ta numpy.isscalar()funkcja cierpi na szereg niemożliwych do pogodzenia wad projektowych i prawdopodobnie będzie przestarzała w przyszłości. Parafrazując oficjalną dokumentację : „W prawie wszystkich przypadkach np.ndim(x) == 0należy użyć zamiast np.isscaler(x), ponieważ pierwsza zwróci również prawdę dla tablic 0d”. numpy.isscalar()Zatem solidną, kompatybilną z przodu alternatywą byłoby trywialne zawijanie numpy.ndim(): np.def is_scalar(obj): return np.ndim(obj) == 0
Cecil Curry
W rzeczywistości nie należy tego oceniać, ponieważ np.isscalarjest mylące. Oficjalny dokument sugeruje używanie np.array.ndimwszędzie, tzn. np.isscalar(np.array(12))Jest fałszywy, podczas gdy powinien być uważany za skalarny, ponieważ np.array(12).ndimwynosi 0.
knh190
17

Chociaż podejście @ jamylak jest lepsze, tutaj jest podejście alternatywne

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True
Sukrit Kalra
źródło
2
Byłoby wspaniale, gdyby osoba, która głosowała za odpowiedzią, również podała powód.
Sukrit Kalra
faktycznie przegłosowałem, ale potem zdałem sobie sprawę, że to nie działa w 2.7: >>> p = [] >>> wpisz (p) na (liście) Traceback (ostatnie połączenie ostatnio): Plik „<stdin>” , wiersz 1, w <module>
Oleg Gryb
@OlegGryb: Spróbuj type(p) in (list, ).
Sukrit Kalra
ah, to krotka po prawej stronie, a nie lista, mam ją, dziękuję i działa teraz. Żałuję, nie mogę głosować 2 razy - najlepsze jak dotąd rozwiązanie :)
Oleg Gryb
3

Inne alternatywne podejście (użycie właściwości nazwy klasy ):

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

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

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

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

Nie musisz niczego importować.

Marek
źródło
3

Oto najlepsze podejście, jakie znalazłem: Sprawdź istnienie __len__i __getitem__.

Możesz zapytać dlaczego? Przyczyny obejmują:

  1. Popularna metoda isinstance(obj, abc.Sequence)zawodzi w przypadku niektórych obiektów, w tym Tensora PyTorcha, ponieważ nie są one implementowane __contains__.
  2. Niestety w kolekcji Python.abc nie ma nic, co sprawdza tylko __len__i __getitem__które są minimalnymi metodami dla obiektów podobnych do tablicy.
  3. Działa na listach, krotkach, ndarray, Tensor itp.

Więc bez zbędnych ceregieli:

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

Zauważ, że dodałem parametry domyślne, ponieważ przez większość czasu możesz chcieć traktować ciągi jako wartości, a nie tablice. Podobnie w przypadku krotek.

Shital Shah
źródło
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
źródło
2

Możesz sprawdzić typ danych zmiennej.

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

To da ci umieścić jako typ danych P.

<type 'int'>

Abyś mógł odróżnić, że jest to liczba całkowita lub tablica.

unnati patil
źródło
2

Dziwi mnie, że takie podstawowe pytanie nie wydaje się mieć bezpośredniej odpowiedzi w pythonie. Wydaje mi się, że prawie wszystkie proponowane odpowiedzi używają pewnego rodzaju sprawdzania typów, co zwykle nie jest zalecane w pythonie i wydają się ograniczone do konkretnego przypadku (zawodzą przy różnych typach liczbowych lub ogólnych iterowalnych obiektach, które nie są krotkami ani listami).

Dla mnie lepiej działa importowanie numpy i używanie array.size, na przykład:

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

Uwaga:

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

Out[5]: 2

ale:

>>> 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
źródło
Dziwi mnie również fakt, że żaden z nich nie wydaje się mieć do czynienia z generatorami.
RhysC,
2

Po prostu użyj sizezamiast 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
Mathieu Villion
źródło
2
NameError: nazwa „wielkość” nie jest zdefiniowane
thang
1
To prawda. Używałem rozmiaru numpy, nie zauważając go. Potrzebujesz: od numpy rozmiaru importu
Mathieu Villion
2
np.size(5)i np.size([5])oba są == 1, więc to nie rozróżnia poprawnie typu (tj. identyfikuje skalar), co moim zdaniem jest celem.
Michael
To interesująca uwaga. Oryginalne pytanie odnosi się do isscalar, która jest funkcją Matlaba. W Matlabie absolutnie nie ma różnicy między skalarem a tablicą o rozmiarze 1, może to być wektor lub tablica N-dim. IMHO, to plus dla Matlaba.
Mathieu Villion
0

preds_test [0] ma kształt (128, 128, 1) Pozwala sprawdzić typ danych za pomocą isinstance () funkcja isinstance przyjmuje 2 argumenty. 1. argument to dane 2. argument to typ danych isinstance (preds_test [0], np.ndarray) daje Output jako True. Oznacza to, że preds_test [0] jest tablicą.

Sumanth Meenan
źródło
0

Aby odpowiedzieć na pytanie w tytule, bezpośrednim sposobem ustalenia, czy zmienna jest skalarem, jest próba przekształcenia jej w zmiennoprzecinkową. Jeśli tak TypeError, to nie jest.

N = [1, 2, 3]
try:
    float(N)
except TypeError:
    print('it is not a scalar')
else:
    print('it is a scalar')
Krążek do hokeja
źródło