python: cómo identificar si una variable es una matriz o un escalar

7 minutos de lectura

Tengo una función que toma el argumento. NBins. Quiero hacer una llamada a esta función con un escalar 50 o una matriz [0, 10, 20, 30]. ¿Cómo puedo identificar dentro de la función, cuál es la longitud de NBins ¿es? o dicho de otra manera, si es un escalar o un vector?

Intenté esto:

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

Como ves, no puedo aplicar len a Pya que no es una matriz…. ¿Hay algo como isarray o isscalar en pitón?

Gracias

  • ¿Has probado a probarlo? type?

    – Sukrit Kalra

    29 de mayo de 2013 a las 6:34

avatar de usuario
jamilak

>>> import collections.abc
>>> isinstance([0, 10, 20, 30], collections.abc.Sequence)
True
>>> isinstance(50, collections.abc.Sequence)
False

Nota: isinstance también admite una tupla de clases, verifique type(x) in (..., ...) debe evitarse y es innecesario.

También puede querer comprobar not isinstance(x, (str, unicode))

Como lo señaló @ 2080 y también aquí, esto no funcionará para numpy arreglos p.ej.

>>> import collections.abc
>>> import numpy as np
>>> isinstance((1, 2, 3), collections.abc.Sequence)
True
>>> isinstance(np.array([1, 2, 3]), collections.abc.Sequence)
False

En cuyo caso, puede probar la respuesta de @ jpaddison3:

>>> hasattr(np.array([1, 2, 3]), "__len__")
True
>>> hasattr([1, 2, 3], "__len__")
True
>>> hasattr((1, 2, 3), "__len__")
True

Sin embargo, como se señaló aquí, esto tampoco es perfecto y clasificará incorrectamente (al menos según yo) los diccionarios como secuencias mientras que isinstance con collections.abc.Sequence clasifica correctamente:

>>> hasattr({"a": 1}, "__len__")
True
>>> from numpy.distutils.misc_util import is_sequence
>>> is_sequence({"a": 1})
True
>>> isinstance({"a": 1}, collections.abc.Sequence)
False

Puede personalizar su solución a algo como esto, agregar más tipos a isinstance dependiendo de sus necesidades:

>>> isinstance(np.array([1, 2, 3]), (collections.abc.Sequence, np.ndarray))
True
>>> isinstance([1, 2, 3], (collections.abc.Sequence, np.ndarray))
True

  • gracias, no me imaginaba invertir list para obtener falso para escalares … gracias

    – otmezger

    29 de mayo de 2013 a las 6:39

  • Si bien esta es una gran respuesta, collections.Sequence es un ABC para cadena también, por lo que debe tenerse en cuenta. estoy usando algo como if type(x) is not str and isinstance(x, collections.Sequence):. Esto no es genial, pero es confiable.

    – bbanne10

    04/08/2014 a las 19:47

  • @bbenne10 claro, pero evita typey también comprobar not isinstance(x, (str, unicode)) en pitón 2

    – Jamylak

    10 de febrero de 2015 a las 11:04

  • ¿Por qué dijiste que “verificar tipo (x) en (…, …) debe evitarse y es innecesario”? Si usted lo dice, sería muy amable de explicar por qué, tal vez no soy el único que se pregunta por qué debería evitarse.

    –Olivier Pons

    30 de mayo de 2017 a las 7:50

  • collections.Sequence –> collections.abc.Sequence mayo ser requerido en Python 3.9 o 3.10.

    –Bob Stein

    19 de agosto de 2020 a las 11:15

Las respuestas anteriores asumen que la matriz es una lista estándar de Python. Como alguien que usa numpy con frecuencia, recomendaría una prueba muy pitónica de:

if hasattr(N, "__len__")

  • las cuerdas tienen un __len__ atributo (así que supongo, técnicamente no es un tipo escalar)

    – xofer

    17 abr 2014 a las 16:49


  • if hasattr(N, '__len__') and (not isinstance(N, str)) daría cuenta correctamente de las cadenas.

    – apdnu

    13/10/2014 a las 19:32

  • También cuenta para dict en Python 3

    –Bruno Henrique

    24 de enero de 2016 a las 1:06

avatar de usuario
scottclow

Combinando las respuestas de @jamylak y @ jpaddison3 juntas, si necesita ser robusto contra matrices numpy como entrada y manejarlas de la misma manera que las listas, debe usar

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

Esto es robusto frente a las subclases de listas, tuplas y matrices numpy.

Y si también quiere ser robusto contra todas las demás subclases de secuencia (no solo lista y tupla), use

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

¿Por qué deberías hacer las cosas de esta manera con isinstance y no comparar type(P) con un valor objetivo? Aquí hay un ejemplo, donde hacemos y estudiamos el comportamiento de NewListuna subclase trivial de list.

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

A pesar de x y y comparándolos como iguales, manejándolos por type daría lugar a un comportamiento diferente. Sin embargo, desde x es una instancia de una subclase de listusando isinstance(x,list) da el comportamiento deseado y trata x y y de la misma manera.

  • Esta es la respuesta que más se adapta a mis necesidades. Acabo de agregar set, también. Porque no quiero ser robusto contra los dictados. isinstance(P, (list, tuple, set, np.ndarray))

    – Santiago

    9 de febrero de 2020 a las 15:53


Si bien el enfoque de @jamylak es el mejor, aquí hay un enfoque alternativo

>>> N=[2,3,5]
>>> P = 5
>>> type(P) in (tuple, list)
False
>>> type(N) in (tuple, list)
True

  • Sería mejor y un ejemplo:>>> np.isscalar('abcd') devoluciones True.

    – Syrtis Mayor

    19 de marzo de 2016 a las 8:03

  • ¡Gracias! este es un ejemplo mucho más general que cualquiera de los anteriores y debe preferirse. También es una respuesta directa a la pregunta del OP.

    – Cristóbal Sifón

    24 de junio de 2018 a las 4:28

  • Agradable. Aunque un problema es que isscalar (Ninguno) devuelve Falso. Numpy implementa esto como return (isinstance(num, generic) or type(num) in ScalarType or isinstance(num, numbers.Number))

    – Shital Shah

    8 de enero de 2019 a las 20:24

  • No, lamentablemente. los numpy.isscalar() función sufre una serie de defectos de diseño irreconciliables y se probablemente quedar en desuso en alguna revisión futura. Parafrasear documentación oficial: “En casi todos los casos np.ndim(x) == 0 debe usarse en lugar de np.isscaler(x)ya que el primero también devolverá correctamente verdadero para matrices 0d”. Una alternativa robusta compatible con versiones posteriores a numpy.isscalar() sería así envolver trivialmente numpy.ndim(): p.ej, def is_scalar(obj): return np.ndim(obj) == 0

    –Cecil Curry

    28 de febrero de 2019 a las 3:55


  • En realidad, esto no debería ser votado porque np.isscalar es confuso. El documento oficial sugirió usar np.array.ndim en todas partes, es decir np.isscalar(np.array(12)) es Falso mientras que debe ser considerado como escalar ya que np.array(12).ndim es 0

    – knh190

    4 de junio de 2019 a las 7:15

Otro enfoque alternativo (uso de clase nombre propiedad):

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

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

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

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

No es necesario importar nada.

Aquí está el mejor enfoque que he encontrado: Comprobar la existencia de __len__ y __getitem__.

Usted puede preguntar por qué? Las razones incluyen:

  1. El método popular isinstance(obj, abc.Sequence) falla en algunos objetos, incluido el tensor de PyTorch porque no implementan __contains__.
  2. Desafortunadamente, no hay nada en el archivo collections.abc de Python que verifique solo __len__ y __getitem__ que creo que son métodos mínimos para objetos tipo matriz.
  3. Funciona en lista, tupla, ndarray, Tensor, etc.

Así que sin más preámbulos:

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

Tenga en cuenta que he agregado parámetros predeterminados porque la mayoría de las veces es posible que desee considerar las cadenas como valores, no como matrices. Del mismo modo para las tuplas.

avatar de usuario
suhailvs

>>> N=[2,3,5]
>>> P = 5
>>> type(P)==type(0)
True
>>> type([1,2])==type(N)
True
>>> type(P)==type([1,2])
False

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad