¿Cuál es la forma canónica de verificar el tipo en Python?

1504

¿Cuál es la mejor forma de comprobar si un objeto determinado es de un tipo determinado? ¿Qué tal comprobar si el objeto hereda de un tipo determinado?

Digamos que tengo un objeto o. ¿Cómo verifico si es un str?

4
  • 9
    Bueno, el enfoque canónico en Python es no verificar el tipo en absoluto (a menos que esté depurando). Por lo general, solo intenta usarlo como una cadena (por ejemplo, concatenar con otras cadenas, imprimir en la consola, etc.); si cree que podría fallar, use try / except o hasattr. Dicho esto, la respuesta aceptada es la forma canónica de hacer lo que generalmente "no deberías hacer" en el mundo de Python. Para obtener más información, busque en Google "Python duck typing" o lea estos: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/… 11/12/14 a las 20:52
  • 13
    Creo que el Sr. Coombs está pasando por alto ejemplos como clases serializables que no son JSON. Si coloca una gran cantidad de datos a través de una función (en cuyo código no se puede influir), es posible que desee convertir ciertas partes de esos datos a, por ejemplo, un <str> antes de pasarlo. Al menos esa es la forma en que terminó en esta página ... 24 nov 2015 a las 18:45
  • 2
    Parece que la razón más común para pedir esto es que uno quiere distinguir entre cadenas e iterables de cadenas. Esta es una pregunta complicada porque las cadenas son iterables de cadenas: una cadena de un solo carácter es incluso una secuencia de sí misma (la última vez que lo verifiqué, probablemente no se debería confiar en ella). Pero, ¿alguien alguna vez usaría algo parecido a una cuerda? . Entonces, la respuesta a "¿Qué debo hacer para distinguir entre cadenas y otros iterables de cadenas?" es correctamente: "Depende de lo que esté intentando hacer". :-D
    clacke
    28/07/2016 a las 12:22
  • 4
    Las anotaciones de tipo Python ahora son una cosa. Echa un vistazo a mypy
    Sheena
    5 oct 2018 a las 8:54
1780

Para verificar si oes una instancia de stro cualquier subclase de str, use isinstance (esta sería la forma "canónica"):

if isinstance(o, str):

Para comprobar si el tipo de oes 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 integradas en la Referencia de la biblioteca de Python para obtener información relevante.

Una nota más: en este caso, si está utilizando Python 2, es posible que desee utilizar:

if isinstance(o, basestring):

porque esto también capturará cadenas Unicode ( unicodeno es una subclase de str; ambos stry unicodeson subclases de basestring). Tenga en cuenta que basestringya no existe en Python 3, donde hay una separación estricta de cadenas ( str) y datos binarios ( bytes).

Alternativamente, isinstanceacepta una tupla de clases. Esto devolverá Truesi oes una instancia de cualquier subclase de cualquiera de (str, unicode):

if isinstance(o, (str, unicode)):
6
  • 32
    str .__ subclasses __ () solo devuelve las subclases directas de str, y no hace lo mismo que issubclass () o isinstance (). (Para hacer eso, tendría que llamar recursivamente .__ subclases __ (). 30 de septiembre de 2008 a las 12:22
  • 17
    Esta es una buena respuesta, pero creo que realmente debería comenzar con una advertencia de que normalmente no debería hacer esto en Python. Tal como está, parece validar la suposición de que esto es una "cosa canónica para hacer en Python", que no lo es. 11/12/14 a las 20:54
  • 5
    ¿Cuál es la diferencia entre instancia y "exactamente"? Si type(a) is Objectentonces 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? 3 de mayo de 2017 a las 13:49
  • 2
    @mavavilj: a is bsignifica que ayb son exactamente lo mismo, es decir, referencias a la misma entidad en la memoria. Entonces, ay btendría que ser exactamente la misma clase, no subclases, como con isinstance(). Consulte, por ejemplo, stackoverflow.com/a/133024/1072212 9 de junio de 2017 a las 17:47
  • @JonCoombs De acuerdo con el fundamento de PEP 622 , isinstance()es la segunda función incorporada más llamada, después de len(). Creo que tenemos que aceptar que isinstancees Python canónico de facto. 4 de julio de 2020 a las 16:10
230

La forma más Pythonic de verificar el tipo de un objeto es ... no verificarlo.

Dado que Python fomenta el Duck Typing , debe try...exceptusar 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, ¡intente usar su .write()método!

Por supuesto, a veces estas bonitas abstracciones se rompen y isinstance(obj, cls)es lo que necesitas. Pero utilícelo con moderación.

18
  • 84
    En mi humilde opinión, la forma más pitónica es hacer frente a cualquier argumento que se dé. En mi código, a menudo no puedo saber si recibo un objeto o una matriz de objetos, y utilizo la verificación de tipos internamente para convertir un solo objeto en una lista de un elemento. 12 de ene. De 2009 a las 11:21
  • 15
    En lugar de intentar usar su método de escritura, hay ocasiones en las que desea hacer esto sin causar una excepción. En este caso, podría hacer ... if hasattr(ob, "write") and callable(ob.write): O guardar algún acceso a dict ...func = getattr(ob, "write", None) if callable(func): ... 25/08/12 a las 19:41
  • 151
    La mecanografía de pato se trata de usar una biblioteca. La verificación de tipos consiste en escribir una biblioteca. No es el mismo dominio de problemas.
    RickyA
    6/12/12 a las 20:26
  • 19
    @RickyA, no estoy de acuerdo. Duck typing se trata de interactuar con objetos utilizando interfaces con semántica conocida. Esto puede aplicarse al código de la biblioteca o al código que utiliza dicha biblioteca. 16/06/2014 a las 19:27
  • 7
    @ nyuszika7h, en Python3 hasattrsolo suprime un AttributeError - Ver: docs.python.org/3.4/library/functions.html#hasattr 26 de diciembre de 2014 a las 3:46
63

isinstance(o, str)devolverá Truesi oes un stro es de un tipo que hereda str.

type(o) is strvolverá Truesi y solo si oes un str. Devolverá Falsesi oes de un tipo que hereda str.

2
  • 7
    Por supuesto, esto fallará si el objeto no es una instancia de 'str', sino algo parecido a una cadena. Como unicode, mmap, UserString o cualquier otro tipo definido por el usuario. El enfoque habitual en Python es no realizar comprobaciones de tipo. 30 de septiembre de 2008 a las 11:07
  • 2
    Esto es muy útil. Porque la diferencia entre isinstancey type(var) == type('')no está clara. 12 de ene. De 2009 a las 11:18
36

Una vez formulada y respondida la pregunta, se agregaron sugerencias de tipo a Python . Las sugerencias de tipo en Python permiten verificar los tipos, pero de una manera muy diferente a los lenguajes escritos estáticamente. Las sugerencias de tipo en Python asocian los tipos esperados de argumentos con funciones como datos accesibles en tiempo de ejecución asociados con funciones y esto permite verificar 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 que se produzca un error cuando el script se ejecuta normalmente. Sin embargo, agrega atributos a la función que describen los tipos esperados que otros programas pueden consultar y usar para verificar errores de tipo.

Uno de estos otros programas que se puede utilizar para encontrar el tipo de error es mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Es posible que deba instalar mypydesde 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 tipado 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, ¿el argumento realmente necesita ser del listtipo exacto o hay algo iterable suficiente?).

La ventaja de la verificación de tipo explícita es que puede detectar errores antes y dar mensajes de error más claros que la mecanografía de pato. Los requisitos exactos de un tipo de pato solo se pueden expresar con documentación externa (es de esperar que sea completa y precisa) y los errores de tipos incompatibles pueden ocurrir lejos de donde se originan.

Las sugerencias de tipos 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 habitual del código.

El typingpaquete 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 Iterabley Callablepara sugerencias para especificar la necesidad de cualquier tipo con esos comportamientos.

Si bien las sugerencias de tipo son la forma más Pythonic de verificar los tipos, a menudo es aún más Pythonic 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 está deliberando sobre cuándo son la solución más Pythonic. Una comparación relativamente poco controvertida pero muy general: las sugerencias de tipo brindan una forma de documentación que se puede hacer cumplir, permiten que el código se genere más temprano y errores más fáciles de entender, puede detectar errores que el tipeo de pato no puede y se pueden verificar estáticamente (de manera inusual sentido pero aún está fuera del tiempo de ejecución). Por otro lado, el tipado pato ha sido el método Pythonic durante mucho tiempo, no impone la sobrecarga cognitiva del tipado estático, es menos detallado y aceptará todos los tipos viables y más.

3
  • 4
    -1: mypy se llama específicamente a sí mismo un "verificador de tipo estático", por lo que no estoy seguro de dónde sacaste "la verificación de tipo debe realizarse en tiempo de ejecución".
    Kevin
    12/08/18 a las 0:48
  • @Kevin En retrospectiva, esa fue una digresión innecesaria, pero para profundizar más, las sugerencias de tipo de Python se convierten en datos de tiempo de ejecución y mypyes un módulo de Python que se usa importlibpara acceder a esos datos. Si esto es una "verificación de tipo estática" es una cuestión filosófica, pero es diferente de lo que la mayoría esperaría, ya que están involucrados el intérprete de lenguaje normal y la maquinaria de importación. 12/08/18 a las 6:20
  • 5
    Eso tampoco es cierto. Se utiliza typed_ast, que a su vez es sólo un clon de AST con características adicionales. ast no importa módulos; los analiza en un árbol de sintaxis abstracto.
    Kevin
    12/08/18 a las 15:56
17

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()
13
  • 38
    Incluso con la verificación de tipos, puede crear 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 que solo supervisara a su hijo (y probara a fondo sus juguetes :) 20 feb 2013 a las 15:30
  • 40
    Las bombas no hablan. No agregue métodos sin sentido y esto no sucederá. 18 mar '14 a las 11:04
  • 7
    @Dmitry, esta es la crítica común de Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... básicamente estás diciendo que cualquier interfaz para la que el lenguaje no aplica la semántica es mala. Creo que este es más el enfoque de Java. El objetivo de la escritura pato de Python es que solo funciona cuando existe una convención comúnmente aceptada sobre lo que significan interfaces específicas. Por ejemplo, podría crear una gran cantidad de código Python anulando el __file__atributo (comúnmente utilizado para identificar objetos similares a archivos) para que signifique algo más. 16/06/2014 a las 19:38
  • 2
    Todo esto se reduce al viejo chiste "Doctor, me duele cuando hago esto". ... "Entonces no hagas eso". Insatisfactorio para alguien que está acostumbrado a "si se compila, se ejecuta", pero es por eso que la obsesión por probar surgió en el mundo del lenguaje dinámico.
    clacke
    28/07/2016 a las 11:46
  • 1
    @clacke básicamente, es demasiado caro aplicar tipos en tiempo de ejecución estrictamente porque TODO debe ser un objeto (para mapear desde una cadena a cualquier tipo posible), y demasiado conveniente para no tener tipo de pato porque el tipo de pato permite técnicas de creación de prototipos realmente poderosas que superan las cosas que normalmente son muy difíciles de hacer con interfaces rígidas. Además, cualquier lenguaje estático se enfrenta a un punto en el que necesita crear tipificación pato a través de bibliotecas dinámicas, evaluación y encadenamiento, o interfaces, y estas cosas no lo hacen inherentemente malo, solo muy poderoso.
    Dmitry
    28/07/2016 a las 12:02
14

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'
1
  • Gracias, este es el código secreto que quería cuando lo mostraba como comentario para el usuario. Me tomó demasiado tiempo encontrar esto ... 30/09/19 a las 20:58
12
isinstance(o, str)

Enlace a documentos

1
  • 3
    Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada.
    EKons
    9 de agosto de 2016 a las 8:59
7

Creo que lo bueno de usar un lenguaje dinámico como Python es que realmente no debería tener que verificar algo así.

Simplemente llamaría a los métodos requeridos en su objeto y capturaría un archivo 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 un objeto. Esto, a su vez, se puede pasar a casi cualquier método que lea de un archivo, porque implementa el mismo read()método que un archivo real.

Pero estoy seguro de que hay un momento y un lugar para usar isinstance(), de lo contrario probablemente no estaría allí :)

1
  • Un buen ejemplo de cuándo debe usarlo es si está analizando un objeto json dinámico. No sabe de antemano si un campo es una cadena o un diccionario.
    Gray
    9/01/19 a las 20:34
7

Para validaciones de tipo más complejas, me gusta el enfoque de typeguard de validación 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) 
5

Para Hugo:

Probablemente quiera decir en listlugar de array, pero eso apunta a todo el problema con la verificación de tipos: no desea saber si el objeto en cuestión es una lista, desea saber si es algún tipo de secuencia o si es un solo objeto. Intenta usarlo como una secuencia.

Digamos 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 a menudo se piensa que una cadena es un solo objeto, pero también es una secuencia de caracteres. Peor que eso, ya que en realidad es 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 [ ]alrededor de su valor único cuando lo transfiere si es necesario.

(Aunque esto puede causar errores con las cadenas, ya que parecen (son) secuencias).

4

En Python 3.10 , puede usarlo |en isinstance:

>>> isinstance('1223', int | str) 
True

>>> isinstance('abcd', int | str) 
True
2

La respuesta aceptada responde a la pregunta en el sentido de que proporciona las respuestas a las preguntas formuladas.

Q: What is the best way to check whether a given object is of a given type? How about checking whether the object inherits from a given type?

A: Use isinstance, issubclass, type to check based on types.

Sin embargo, como otras respuestas y comentarios se apresuran a señalar, hay mucho más en la idea de "verificación de tipos" que en Python. Desde la adición de Python 3 y las sugerencias de tipo , también ha cambiado mucho. A continuación, repaso algunas de las dificultades con la verificación de tipos, la escritura de pato y el manejo de excepciones. Para aquellos que piensan que la verificación de tipos no es lo que se necesita (generalmente no lo es, pero estamos aquí), también señalo cómo se pueden usar las sugerencias de tipos en su lugar.

Comprobación de tipo

La verificación de tipos no siempre es algo apropiado para hacer en Python. Considere el siguiente ejemplo:

def sum(nums):
    """Expect an iterable of integers and return the sum."""
    result = 0
    for n in nums:
        result += n
    return result

Para comprobar si la entrada es iterable de enteros, nos encontramos con un problema importante. La única forma de verificar si cada elemento es un número entero sería recorrer para verificar cada elemento. Pero si recorremos todo el iterador, no quedará nada para el código previsto. Tenemos dos opciones en este tipo de situaciones.

  1. Verifique mientras recorremos el bucle.

  2. Verifique de antemano pero guarde todo como lo verificamos.

La opción 1 tiene la desventaja de complicar nuestro código, especialmente si necesitamos realizar comprobaciones similares en muchos lugares. Nos obliga a mover la verificación de tipos desde la parte superior de la función a todos los lugares donde usamos el iterable en nuestro código.

La opción 2 tiene la desventaja obvia de que destruye todo el propósito de los iteradores. El punto es no almacenar los datos porque no deberíamos necesitarlo.

También se podría pensar que verificar si verificar todos los elementos es demasiado, entonces quizás podamos verificar si la entrada en sí es del tipo iterable, pero en realidad no hay ninguna clase base iterable. Cualquier tipo de implementación __iter__es iterable.

Manejo de excepciones y tipificación de pato

Un enfoque alternativo sería renunciar por completo a la verificación de tipos y centrarse en el manejo de excepciones y la escritura de pato. Es decir, envuelva su código en un bloque try-except y detecte cualquier error que ocurra. Alternativamente, no haga nada y deje que las excepciones surjan naturalmente de su código.

Aquí hay una forma de detectar una excepción.

def sum(nums):
    """Try to catch exceptions?"""
    try:
        result = 0
        for n in nums:
            result += n
        return result
    except TypeError as e:
        print(e)

En comparación con las opciones anteriores, esto es ciertamente mejor. Comprobamos mientras ejecutamos el código. Si hay un TypeErrorlugar, lo sabremos. No tenemos que colocar una marca en todos los lugares donde recorremos la entrada. Y no tenemos que almacenar la entrada mientras iteramos sobre ella.

Además, este enfoque permite la escritura de pato. En lugar de verificar specific types, hemos pasado a verificar specific behaviorsy buscar cuando la entrada no se comporte como se esperaba (en este caso, recorriendo numsy pudiendo agregar n).

Sin embargo, las razones exactas que hacen que el manejo de excepciones sea agradable también pueden ser su perdición.

  1. A floatno es un int, pero satisface los requisitos de comportamiento para funcionar.

  2. También es una mala práctica envolver todo el código con un bloque try-except.

Al principio, estos pueden no parecer problemas, pero aquí hay algunas razones que pueden hacer que cambie de opinión.

  1. Un usuario ya no puede esperar que nuestra función devuelva un int. Esto puede romper el código en otros lugares.

  2. Dado que las excepciones pueden provenir de una amplia variedad de fuentes, el uso de try-except en todo el bloque de código puede terminar detectando excepciones que no pretendía. Solo queríamos comprobar si numsera iterable y tenía elementos enteros.

  3. Idealmente, nos gustaría detectar excepciones de nuestros generadores de código y generar, en su lugar, excepciones más informativas. No es divertido cuando se genera una excepción desde el código de otra persona sin otra explicación que no sea una línea que no escribiste y que TypeErrorocurrió.

Para arreglar el manejo de excepciones en respuesta a los puntos anteriores, nuestro código se convertiría en esta ... abominación.

def sum(nums):
    """
    Try to catch all of our exceptions only.
    Re-raise them with more specific details.
    """
    result = 0

    try:
        iter(nums)
    except TypeError as e:
        raise TypeError("nums must be iterable")

    for n in nums:
        try:
            result += int(n)
        except TypeError as e:
            raise TypeError("stopped mid iteration since a non-integer was found")

    return result

Puedes ver un poco a dónde va esto. Cuanto más intentemos comprobar "correctamente" las cosas, peor se verá nuestro código. Comparado con el código original, esto no es legible en absoluto.

Podríamos argumentar que tal vez esto sea un poco extremo. Pero, por otro lado, este es solo un ejemplo muy simple. En la práctica, su código probablemente sea mucho más complicado que esto.

Sugerencias de tipo

Hemos visto lo que sucede cuando intentamos modificar nuestro pequeño ejemplo para "habilitar la verificación de tipos". En lugar de centrarse en tratar de forzar tipos específicos, la sugerencia de tipo permite una forma de aclarar los tipos para los usuarios.

from typing import Iterable

def sum(nums: Iterable[int]) -> int:
    result = 0
    for n in nums:
        result += n
    return result

Estas son algunas de las ventajas de usar sugerencias de tipo.

  • ¡El código realmente se ve bien ahora!

  • Su editor puede realizar un análisis de tipo estático si utiliza sugerencias de tipo.

  • Se almacenan en la función / clase, lo que los hace dinámicamente utilizables, por ejemplo, typeguardy dataclasses.

  • Aparecen para funciones cuando se usan help(...).

  • No es necesario verificar la cordura si su tipo de entrada es correcto en función de una descripción o, peor aún, la falta de ella.

  • Puede "escribir" una pista basada en la estructura, por ejemplo, "¿tiene este atributo?" sin requerir subclases por parte del usuario.

¿La desventaja de escribir sugerencias?

  • Las sugerencias de tipo no son más que sintaxis y texto especial por sí mismos. No es lo mismo que la verificación de tipos .

En otras palabras, en realidad no responde a la pregunta porque no proporciona verificación de tipos. Independientemente, sin embargo, si está aquí para la verificación de tipos, también debería incluir sugerencias de tipo. Por supuesto, si ha llegado a la conclusión de que la verificación de tipos no es realmente necesaria, pero desea algo parecido a la mecanografía, las sugerencias de escritura son para usted.

-3

Una forma sencilla de comprobar el tipo es compararlo con algo cuyo tipo conozca.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
-6

Creo que la mejor forma es escribir bien las variables. Puede hacer esto usando la biblioteca de "mecanografía".

Ejemplo:

from typing import NewType
UserId = NewType ('UserId', int)
some_id = UserId (524313`)

Consulte https://docs.python.org/3/library/typing.html .