¿Cuál es la mejor manera de verificar si un objeto dado es de un tipo dado? ¿Qué hay de verificar si el objeto hereda de un tipo dado?
Digamos que tengo un objeto o
. ¿Cómo verifico si es un str
?
¿Cuál es la mejor manera de verificar si un objeto dado es de un tipo dado? ¿Qué hay de verificar si el objeto hereda de un tipo dado?
Digamos que tengo un objeto o
. ¿Cómo verifico si es un str
?
Respuestas:
Para verificar si o
es una instancia str
o alguna subclase de str
, use isinstance (esta sería la forma "canónica"):
if isinstance(o, str):
Para verificar si el tipo de o
es exactamente str
(excluir subclases):
if type(o) is str:
Lo siguiente también funciona y puede ser útil en algunos casos:
if issubclass(type(o), str):
Consulte Funciones incorporadas en la Referencia de la biblioteca de Python para obtener información relevante.
Una nota más: en este caso, si está usando Python 2, es posible que desee usar:
if isinstance(o, basestring):
porque esto también capturará cadenas Unicode ( unicode
no es una subclase de str
; ambas str
y unicode
son subclases de basestring
). Tenga en cuenta que basestring
ya no existe en Python 3, donde hay una separación estricta de cadenas ( str
) y datos binarios ( bytes
).
Alternativamente, isinstance
acepta una tupla de clases. Esto devolverá True
si o
es una instancia de cualquier subclase de cualquiera de (str, unicode)
:
if isinstance(o, (str, unicode)):
type(a) is Object
entonces no es cierto también eso isinstance(a, Object)
. Sin embargo, si type(a) is SubClassOfObject
, entonces type(a) is Object == False
, pero isinstance(a, Object) == True
. ¿Derecha?
a is b
significa que ayb son exactamente lo mismo, es decir, referencias a la misma entidad en la memoria. Entonces, a
y b
tendría que ser exactamente la misma clase, no subclases, como con isinstance()
. Ver por ejemplo stackoverflow.com/a/133024/1072212
La forma más pitónica de verificar el tipo de un objeto es ... no verificarlo.
Dado que Python alienta a Duck Typing , solo debe try...except
usar los métodos del objeto de la forma en que desea usarlos. Entonces, si su función está buscando un objeto de archivo grabable, no verifique que sea una subclase de file
, ¡solo intente usar su .write()
método!
Por supuesto, a veces estas agradables abstracciones se descomponen y isinstance(obj, cls)
es lo que necesitas. Pero use con moderación.
if hasattr(ob, "write") and callable(ob.write):
O guardar algún acceso dict ...func = getattr(ob, "write", None)
if callable(func): ...
hasattr
solo suprime un AttributeError - Ver: docs.python.org/3.4/library/functions.html#hasattr
isinstance(o, str)
regresará True
si o
es str
o es de un tipo que hereda de str
.
type(o) is str
volverá True
si y solo si o
es un str. Volverá False
si o
es de un tipo que hereda de str
.
isinstance
y type(var) == type('')
no está clara.
Después de hacer y responder la pregunta, se agregaron sugerencias de tipo a Python . Las sugerencias de tipo en Python permiten que se verifiquen los tipos, pero de una manera muy diferente a los lenguajes estáticos. Las sugerencias de tipo en Python asocian los tipos de argumentos esperados con funciones como datos accesibles en tiempo de ejecución asociados con funciones y esto permite que se verifiquen los tipos. Ejemplo de sintaxis de sugerencia de tipo:
def foo(i: int):
return i
foo(5)
foo('oops')
En este caso, queremos que se active un error foo('oops')
ya que el tipo anotado del argumento es int
. La sugerencia de tipo agregada no provoca un error cuando el script se ejecuta normalmente. Sin embargo, agrega atributos a la función que describe los tipos esperados que otros programas pueden consultar y usar para verificar los errores de tipo.
Uno de estos otros programas que se pueden usar para encontrar el error de tipo es mypy
:
mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
(Es posible que necesite instalar mypy
desde su administrador de paquetes. No creo que venga con CPython pero parece tener cierto nivel de "oficialidad").
La verificación de tipos de esta manera es diferente de la verificación de tipos en lenguajes compilados de tipo estático. Debido a que los tipos son dinámicos en Python, la verificación de tipos debe realizarse en tiempo de ejecución, lo que impone un costo, incluso en los programas correctos, si insistimos en que suceda en cada oportunidad. Las verificaciones de tipo explícitas también pueden ser más restrictivas de lo necesario y causar errores innecesarios (por ejemplo, ¿es necesario que el argumento sea exactamente del list
tipo o es algo iterable suficiente?).
La ventaja de la verificación explícita de tipos es que puede detectar errores antes y dar mensajes de error más claros que la escritura de pato. Los requisitos exactos de un tipo de pato solo se pueden expresar con documentación externa (es de esperar que sea exhaustiva y precisa) y los errores de tipos incompatibles pueden ocurrir lejos de donde se originan.
Las sugerencias de tipo de Python están destinadas a ofrecer un compromiso en el que los tipos se pueden especificar y verificar, pero no hay ningún costo adicional durante la ejecución de código habitual.
El typing
paquete ofrece variables de tipo que se pueden usar en sugerencias de tipo para expresar los comportamientos necesarios sin requerir tipos particulares. Por ejemplo, incluye variables como Iterable
y Callable
para sugerencias para especificar la necesidad de cualquier tipo con esos comportamientos.
Si bien las sugerencias de tipo son la forma más pitónica de verificar los tipos, a menudo es aún más pitónico no verificar los tipos en absoluto y confiar en la escritura de pato. Las sugerencias de tipo son relativamente nuevas y el jurado aún no sabe cuándo son la solución más pitónica. Una comparación relativamente poco controvertida pero muy general: las sugerencias de tipo proporcionan una forma de documentación que se puede hacer cumplir, permiten que el código genere errores anteriores y más fáciles de entender, puede detectar errores que no se pueden escribir en el pato y se pueden verificar de forma estática (en un caso inusual sentido, pero todavía está fuera del tiempo de ejecución). Por otro lado, el tipeo de pato ha sido la forma pitónica durante mucho tiempo, no impone la sobrecarga cognitiva del tipeo estático, es menos detallado y aceptará todos los tipos viables y más.
mypy
es un módulo de Python que se utiliza importlib
para acceder a esos datos. Si esto es una "verificación de tipo estático" es una pregunta filosófica, pero es diferente de lo que la mayoría esperaría ya que el intérprete de lenguaje normal y la maquinaria de importación están involucrados.
Aquí hay un ejemplo de por qué escribir pato es malo sin saber cuándo es peligroso. Por ejemplo: Aquí está el código Python (posiblemente omitiendo la sangría adecuada), tenga en cuenta que esta situación se puede evitar cuidando las funciones isinstance y issubclassof para asegurarse de que cuando realmente necesite un pato, no obtenga una bomba.
class Bomb:
def __init__(self):
""
def talk(self):
self.explode()
def explode(self):
print "BOOM!, The bomb explodes."
class Duck:
def __init__(self):
""
def talk(self):
print "I am a duck, I will not blow up if you ask me to talk."
class Kid:
kids_duck = None
def __init__(self):
print "Kid comes around a corner and asks you for money so he could buy a duck."
def takeDuck(self, duck):
self.kids_duck = duck
print "The kid accepts the duck, and happily skips along"
def doYourThing(self):
print "The kid tries to get the duck to talk"
self.kids_duck.talk()
myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
class EvilDuck(Duck)
y anular talk (). O más probablemente, class ChineseCancerDuck(Duck)
con un efecto secundario desagradable que no aparece hasta años después. Sería mejor supervisar a tu hijo (y probar a fondo sus juguetes :)
__file__
atributo (comúnmente utilizado para identificar objetos similares a archivos) para que signifique algo más.
isinstance(o, str)
Creo que lo bueno de usar un lenguaje dinámico como Python es que realmente no deberías tener que verificar algo así.
Simplemente llamaría a los métodos requeridos en su objeto y capturaría un AttributeError
. Más adelante, esto le permitirá llamar a sus métodos con otros objetos (aparentemente no relacionados) para realizar diferentes tareas, como burlarse de un objeto para probarlo.
Lo he usado mucho cuando obtengo datos de la web con los urllib2.urlopen()
que devuelve un archivo como objeto. Esto a su vez se puede pasar a casi cualquier método que lea de un archivo, ya que implementa el mismo read()
método que un archivo real.
Pero estoy seguro de que hay un momento y lugar para usar isinstance()
, de lo contrario, probablemente no estaría allí :)
Para validaciones de tipos más complejas, me gusta el enfoque de validación de typeguard basado en anotaciones de sugerencias de tipo python:
from typeguard import check_type
from typing import List
try:
check_type('mylist', [1, 2], List[int])
except TypeError as e:
print(e)
Puede realizar validaciones muy complejas de una manera muy limpia y legible.
check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo)
Puede verificar el tipo de una variable usando __name__ de un tipo.
Ex:
>>> a = [1,2,3,4]
>>> b = 1
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
A Hugo:
Probablemente quieras decir en list
lugar dearray
, pero eso apunta al problema completo con la verificación de tipos: no quieres saber si el objeto en cuestión es una lista, quieres saber si es algún tipo de secuencia o si es un solo objeto. Intenta usarlo como una secuencia.
Supongamos que desea agregar el objeto a una secuencia existente, o si es una secuencia de objetos, agréguelos todos
try:
my_sequence.extend(o)
except TypeError:
my_sequence.append(o)
Un truco con esto es si está trabajando con cadenas y / o secuencias de cadenas; eso es complicado, ya que una cadena a menudo se considera como un solo objeto, pero también es una secuencia de caracteres. Peor que eso, ya que es realmente una secuencia de cadenas de una sola longitud.
Por lo general, elijo diseñar mi API para que solo acepte un valor único o una secuencia, lo que facilita las cosas. No es difícil poner un [ ]
valor único alrededor de usted cuando lo pasa si es necesario.
(Aunque esto puede causar errores con las cadenas, ya que se ven como (son) secuencias).
Una forma simple de verificar el tipo es compararlo con algo cuyo tipo conoces.
>>> a = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
Creo que la mejor manera es escribir bien sus variables. Puede hacer esto utilizando la biblioteca "escribiendo".
Ejemplo:
from typing import NewType
UserId = NewType ('UserId', int)
some_id = UserId (524313
) `
Puede verificar con la línea a continuación para verificar qué tipo de carácter es el valor dado:
def chr_type(chrx):
if chrx.isalpha()==True:
return 'alpha'
elif chrx.isdigit()==True:
return 'numeric'
else:
return 'nothing'
chr_type("12)