function - ¿Cuáles son los buenos usos para "Anotaciones de funciones" de Python3?





annotations python-3.x (11)


Solo para agregar un ejemplo específico de un buen uso de mi respuesta here , junto con los decoradores se puede hacer un mecanismo simple para multimedios.

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

y un ejemplo de uso:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

Esto se puede hacer agregando los tipos al decorador como se muestra en la publicación original de Guido , pero anotar los parámetros es mejor ya que evita la posibilidad de una coincidencia incorrecta de los parámetros y tipos.

Nota : en Python, puede acceder a las anotaciones como function.__annotations__ lugar de function.func_annotations ya que el estilo func_* se eliminó en Python 3.

Anotaciones de funciones: PEP-3107

Me encontré con un fragmento de código que demuestra las anotaciones de función de Python3. El concepto es simple, pero no puedo pensar en por qué se implementaron en Python3 o en algún buen uso para ellos. Tal vez SO pueda iluminarme?

Cómo funciona:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

Todo lo que sigue al colon después de un argumento es una 'anotación', y la información que sigue a -> es una anotación para el valor de retorno de la función.

foo.func_annotations devolvería un diccionario:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

¿Cuál es la importancia de tener esto disponible?




La primera vez que vi anotaciones, pensé "¡genial! ¡Finalmente puedo optar por algún tipo de verificación!" Por supuesto, no me había dado cuenta de que las anotaciones en realidad no se hacen cumplir.

Así que decidí escribir un simple decorador de funciones para aplicarlas :

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

Lo agregué a la biblioteca Ensure .




Las anotaciones de funciones son lo que haces de ellas.

Se pueden usar para la documentación:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

Se pueden usar para la verificación previa de condición:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

También vea http://www.python.org/dev/peps/pep-0362/ para una forma de implementar la verificación de tipos.




Ha pasado mucho tiempo desde que se solicitó, pero el fragmento de ejemplo que se da en la pregunta es (como se indica allí también) de PEP 3107 y al final del ejemplo de PEP. También se dan casos de uso que podrían responder a la pregunta desde el punto PEP ver;)

Lo siguiente es citado de PEP3107

Casos de uso

En el curso de la discusión de las anotaciones, se han planteado una serie de casos de uso. Algunos de estos se presentan aquí, agrupados por el tipo de información que transmiten. También se incluyen ejemplos de productos y paquetes existentes que podrían hacer uso de anotaciones.

  • Proporcionando información de tipeo
    • Verificación de tipos ([3], [4])
    • Deje IDE mostrar qué tipos espera y devuelve una función ([17])
    • Función de sobrecarga / funciones genéricas ([22])
    • Puentes de lengua extranjera ([18], [19])
    • Adaptación ([21], [20])
    • Funciones lógicas predicadas
    • Mapeo de consulta
    • Mapeo de parámetros de RPC ([23])
  • Otra información
    • Documentación para parámetros y valores de retorno ([24])

Consulte el PEP para obtener más información sobre puntos específicos (así como sus referencias)




Creo que esto es realmente genial.

Viniendo de una formación académica, puedo decirles que las anotaciones han demostrado ser invaluables para habilitar analizadores estáticos inteligentes para idiomas como Java. Por ejemplo, puede definir semánticas como restricciones de estado, hilos a los que se permite acceder, limitaciones de arquitectura, etc., y hay bastantes herramientas que pueden leerlas y procesarlas para proporcionar garantías más allá de lo que obtienen de los compiladores. Incluso podría escribir cosas que verifiquen condiciones previas / posteriores.

Siento que algo así es especialmente necesario en Python debido a su tipado más débil, pero en realidad no hubo construcciones que lo hicieran sencillo y formaran parte de la sintaxis oficial.

Hay otros usos para las anotaciones más allá de la seguridad. Puedo ver cómo podría aplicar mis herramientas basadas en Java a Python. Por ejemplo, tengo una herramienta que le permite asignar advertencias especiales a los métodos, y le da indicaciones cuando los llama para que lea su documentación (por ejemplo, imagine que tiene un método que no debe invocarse con un valor negativo, pero es no es intuitivo por el nombre). Con las anotaciones, técnicamente podría escribir algo como esto para Python. De forma similar, una herramienta que organiza métodos en una clase grande basada en etiquetas se puede escribir si hay una sintaxis oficial.




Uri ya ha dado una respuesta adecuada, así que aquí hay una menos seria: para que puedas acortar tus documentos.




Python 3.X (solamente) también generaliza la definición de la función para permitir que los argumentos y los valores de retorno se anoten con los valores del objeto para usar en las extensiones .

Su META-data para explicar, para ser más explícito sobre los valores de la función.

Las anotaciones se codifican como :value después del nombre del argumento y antes de un valor predeterminado, y como ->value después de la lista de argumentos.

Se recopilan en un atributo __annotations__ de la función, pero Python no lo considera como especial:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

Fuente: Python Pocket Reference, quinta edición

EJEMPLO:

El módulo typeannotations proporciona un conjunto de herramientas para la verificación de tipos y la inferencia de tipo de código de Python. También proporciona un conjunto de tipos útiles para anotar funciones y objetos.

Estas herramientas están diseñadas principalmente para ser utilizadas por analizadores estáticos como linters, bibliotecas de finalización de código e IDE. Además, se proporcionan decoradores para realizar comprobaciones en tiempo de ejecución. La comprobación de tipos en tiempo de ejecución no siempre es una buena idea en Python, pero en algunos casos puede ser muy útil.

https://github.com/ceronman/typeannotations




Esta es una respuesta tardía, pero AFAICT, el mejor uso actual de anotaciones de funciones es python.org/dev/peps/pep-0484 y MyPy .

Mypy es un verificador de tipo estático opcional para Python. Puede agregar sugerencias de tipo a sus programas de Python utilizando el próximo estándar para anotaciones de tipo introducido en Python 3.5 beta 1 (PEP 484) y use mypy para escribirlos y verifíquelos estáticamente.

Usado así:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b



Las anotaciones se pueden usar para modular fácilmente el código. Por ejemplo, un módulo para un programa que mantengo podría simplemente definir un método como:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

y podríamos pedirle al usuario algo llamado "param1" que es "Necesario para contar" y debería ser "int". Al final, incluso podemos convertir la cadena dada por el usuario al tipo deseado para obtener la experiencia más libre de problemas.

Consulte nuestro objeto de metadatos de función para una clase de fuente abierta que ayuda con esto y puede recuperar automáticamente los valores necesarios y convertirlos al tipo deseado (porque la anotación es un método de conversión). Incluso los IDE muestran autocomplementos correctos y suponen que los tipos están de acuerdo con las anotaciones: un ajuste perfecto.




A pesar de todos los usos que se describen aquí, el único uso exigible y, con toda probabilidad, obligatorio de las anotaciones será para las sugerencias de tipo .

Actualmente no se aplica de ninguna manera pero, a juzgar por PEP 484, las versiones futuras de Python solo permitirán tipos como el valor para las anotaciones.

Citando ¿Qué pasa con los usos existentes de las anotaciones? :

Esperamos que las sugerencias de tipo finalmente se conviertan en el único uso para las anotaciones, pero esto requerirá una discusión adicional y un período de desaprobación después del despliegue inicial del módulo de escritura con Python 3.5. El PEP actual tendrá un estado provisional (vea PEP 411) hasta que se libere Python 3.6. El esquema concebible más rápido introduciría la desaprobación silenciosa de las anotaciones sin insinuación de tipo en 3.6, desaprobación completa en 3.7, y declarar sugerencias de tipo como el único uso permitido de las anotaciones en Python 3.8.

Aunque todavía no he visto ninguna depreciación silenciosa en 3.6, esto bien podría ser tocado a 3.7 en su lugar.

Por lo tanto, a pesar de que podría haber otros casos de uso buenos, es mejor mantenerlos únicamente para la sugerencia de tipo si no desea cambiar todo en un futuro donde esta restricción esté en su lugar.




Son bastante similares con algunas pequeñas diferencias, la primera es una variable que se asigna a una función anónima (Declaración de función) y la segunda es la forma normal de crear una función en JavaScript (Declaración de función anónima), ambas tienen uso, desventajas y ventajas :

1. Expresión de la función

var functionOne = function() {
    // Some code
};

Una expresión de función define una función como parte de una sintaxis de expresión más grande (normalmente una asignación de variable). Funciones definidas a través de Funciones Las expresiones pueden ser nombradas o anónimas. Las expresiones de función no deben comenzar con "función" (por lo tanto, los paréntesis alrededor del ejemplo que se invoca a continuación).

Asignar una variable a una función, significa que no hay Elevación, ya que sabemos que las funciones en JavaScript pueden Elevar, significa que pueden llamarse antes de que se declaren, mientras que las variables deben declararse antes de acceder a ellas, así que en este caso no podemos acceda a la función antes de que se declare, también podría ser una forma de escribir sus funciones, para las funciones que devuelven otra función, este tipo de declaración podría tener sentido, también en ECMA6 y más arriba puede asignar esto a una función de flecha que puede utilizarse para llamar a funciones anónimas, también esta forma de declaración es una mejor manera de crear funciones de Constructor en JavaScript.

2. Declaración de la función

function functionTwo() {
    // Some code
}

Una declaración de función define una variable de función nombrada sin requerir asignación de variable. Las declaraciones de funciones se producen como construcciones independientes y no se pueden anidar dentro de bloques que no son de función. Es útil pensar en ellos como hermanos de declaraciones variables. Al igual que las Declaraciones de variables deben comenzar con "var", las Declaraciones de funciones deben comenzar con "function".

Esta es la forma normal de llamar a una función en JavaScript, se puede llamar a esta función incluso antes de declararla ya que en JavaScript todas las funciones son Hoisted, pero si tiene "uso estricto" no se Hoist como se espera, es una buena manera para llamar a todas las funciones normales que no son grandes en líneas y ninguna es una función constructora.

Además, si necesita más información sobre cómo funciona la elevación en JavaScript, visite el siguiente enlace:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting





python function annotations python-3.x