Agregar valor de parámetro predeterminado con sugerencia de tipo en Python

236

Si tengo una función como esta:

def foo(name, opts={}):
  pass

Y quiero agregar sugerencias de tipo a los parámetros, ¿cómo lo hago? La forma en que supuse me da un error de sintaxis:

def foo(name: str, opts={}: dict) -> str:
  pass

Lo siguiente no arroja un error de sintaxis, pero no parece la forma intuitiva de manejar este caso:

def foo(name: str, opts: dict={}) -> str:
  pass

No puedo encontrar nada en la typingdocumentación o en una búsqueda en Google.

Editar: no sabía cómo funcionaban los argumentos predeterminados en Python, pero por el bien de esta pregunta, mantendré los ejemplos anteriores. En general, es mucho mejor hacer lo siguiente:

def foo(name: str, opts: dict=None) -> str:
  if not opts:
    opts={}
  pass
josh
fuente
44
La última función es la forma correcta. Es la misma forma en que el scalalenguaje también lo hace.
Israel Unterman
14
tiene un tipo predeterminado mutable, que generará problemas
noɥʇʎԀʎzɐɹƆ
vea mi respuesta de actualización, @josh
noɥʇʎԀʎzɐɹƆ
@ noɥʇʎԀʎzɐɹƆ No, a menos que lo esté utilizando, por ejemplo, la memorización. : P
Mateen Ulhaq

Respuestas:

281

Tu segunda forma es correcta.

def foo(opts: dict = {}):
    pass

print(foo.__annotations__)

esto produce

{'opts': <class 'dict'>}

Es cierto que no figura en PEP 484 , pero las sugerencias de tipo son una aplicación de anotaciones de funciones, que están documentadas en PEP 3107. La sección de sintaxis deja en claro que los argumentos de palabras clave funcionan con las anotaciones de funciones de esta manera.

Recomiendo encarecidamente no utilizar argumentos de palabras clave mutables. Más información aquí .

noɥʇʎԀʎzɐɹƆ
fuente
1
Ver legacy.python.org/dev/peps/pep-3107/#syntax . La sugerencia de tipos es solo una aplicación de anotaciones de funciones.
chepner
@chepner cierto. no sabía que PEP 3107 tenía algo sobre argumentos de palabras clave.
noɥʇʎԀʎzɐɹƆ
2
Wow, no sabía sobre los argumentos predeterminados mutables en Python ... especialmente viniendo de Javascript / Ruby, donde los argumentos predeterminados funcionan de manera diferente. No voy a repetir lo que ya se ha dicho ad nauseum sobre SO al respecto, me alegra haberme enterado de esto antes de que me mordiera. ¡Gracias!
josh
66
Siempre me aconsejaron usar None en lugar de un tipo mutable como {} o [] o un objeto predeterminado ya que las mutaciones a ese objeto sin una copia profunda persistirán entre iteraciones.
MrMesees
2
defina suficientes funciones con argumentos de palabras clave mutables y es solo cuestión de tiempo antes de que se encuentre mirando hacia atrás en una sesión de depuración de 4 horas cuestionando sus opciones de vida
Joseph Sheedy
20

Si está usando la escritura (introducida en Python 3.5) puede usar typing.Optional, donde Optional[X]es equivalente a Union[X, None]. Se utiliza para indicar que Nonese permite el valor explícito de . De escribir . Opcional :

def foo(arg: Optional[int] = None) -> None:
    ...
Tomasz Bartkowiak
fuente
2
No debería haber espacios en blanco alrededor de la =en Optional[int] = Nonecomo es la convención para los argumentos de palabras clave no-tipo-insinuado?
actual_panda
7

Hace poco vi esta frase:

def foo(name: str, opts: dict=None) -> str:
    opts = {} if not opts else opts
    pass
Kirkalicious
fuente
Hola @ Kirkalicious, gracias por tu respuesta. ¿Podrías explicar cómo funciona?
Nathan
1
El dict vacío que se pasa como parámetro predeterminado es el mismo dict para cada llamada. Entonces, si la función la muta, la próxima vez será el valor mutado de la última vez. Hacer Noneel valor predeterminado y luego verificar dentro del método evita este problema al asignar un nuevo dict cada vez que se invoca el método.
Ian Goldby