Es cierto que la mayoría de los lenguajes de programación hacen que el orden de los parámetros sea parte del contrato de llamada a la función, pero esto no tiene por qué ser así. ¿Por qué lo haría? Mi comprensión de la pregunta es, entonces, si Python es diferente a otros lenguajes de programación a este respecto. Además de otras buenas respuestas para Python 2, considere lo siguiente:
__named_only_start = object()
def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1):
if _p is not __named_only_start:
raise TypeError("info() takes at most 3 positional arguments")
return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse)
La única forma en que una persona que llama podría proporcionar argumentos spacing
y collapse
posicionalmente (sin excepción) sería:
info(arg1, arg2, arg3, module.__named_only_start, 11, 2)
La convención de no usar elementos privados que pertenecen a otros módulos ya es muy básica en Python. Al igual que con el propio Python, esta convención para los parámetros solo se aplicaría parcialmente.
De lo contrario, las llamadas tendrían que tener el formato:
info(arg1, arg2, arg3, spacing=11, collapse=2)
Una llamada
info(arg1, arg2, arg3, 11, 2)
asignaría el valor 11 al parámetro _p
y una excepción surgida por la primera instrucción de la función.
Caracteristicas:
- Parámetros antes
_p=__named_only_start
se admiten posicionalmente (o por nombre).
- Los parámetros posteriores
_p=__named_only_start
deben proporcionarse solo por el nombre (a menos que __named_only_start
se obtenga y se utilice el conocimiento sobre el objeto centinela especial ).
Pros:
- Los parámetros son explícitos en número y significado (lo último si también se eligen buenos nombres, por supuesto).
- Si el centinela se especifica como primer parámetro, entonces todos los argumentos deben especificarse por nombre.
- Al llamar a la función, es posible cambiar al modo posicional utilizando el objeto centinela
__named_only_start
en la posición correspondiente.
- Se puede anticipar un mejor desempeño que otras alternativas.
Contras:
La verificación se produce durante el tiempo de ejecución, no durante la compilación.
- Uso de un parámetro adicional (aunque no argumento) y una verificación adicional. Pequeña degradación del rendimiento con respecto a las funciones habituales.
- La funcionalidad es un truco sin el apoyo directo del idioma (ver nota a continuación).
- Al llamar a la función, es posible cambiar al modo posicional utilizando el objeto centinela
__named_only_start
en la posición correcta. Sí, esto también puede verse como un profesional.
Tenga en cuenta que esta respuesta solo es válida para Python 2. Python 3 implementa el mecanismo similar, pero muy elegante, compatible con el lenguaje descrito en otras respuestas.
Descubrí que cuando abro mi mente y pienso en ello, ninguna pregunta o decisión de otros parece estúpida, tonta o simplemente tonta. Muy al contrario: normalmente aprendo mucho.
_named_only_start
, es imposible hacer referencia a ella desde un módulo externo, lo que elimina un pros y un contra. (Los guiones bajos iniciales únicos en el ámbito del módulo son privados, IIRC)__named_only_start
y unanamed_only_start
(sin subrayado inicial), la segunda para indicar que el modo nombrado es "recomendado", pero no al nivel de "promoción activa" (ya que uno es público y el otro no lo es). Con respecto a la "privacidad" de_names
comenzar con guiones bajos, el lenguaje no la impone de manera muy estricta: se puede eludir fácilmente mediante el uso de importaciones específicas (no *) o nombres calificados. Es por esto que varios documentos de Python prefieren usar el término "no público" en lugar de "privado".Puede hacerlo de una manera que funcione tanto en Python 2 como en Python 3 , creando un primer argumento de palabra clave "falso" con un valor predeterminado que no ocurrirá "naturalmente". Ese argumento de palabra clave puede estar precedido por uno o más argumentos sin valor:
Esto permitira:
pero no:
Si cambia la función a:
entonces todos los argumentos deben tener palabras clave y
info(odbchelper)
ya no funcionarán.Esto le permitirá colocar argumentos de palabras clave adicionales en cualquier lugar después
_kw
, sin forzarlo a colocarlos después de la última entrada. Esto a menudo tiene sentido, por ejemplo, agrupar cosas lógicamente o ordenar las palabras clave alfabéticamente puede ayudar con el mantenimiento y el desarrollo.Por lo tanto, no es necesario volver a usar
def(**kwargs)
y perder la información de la firma en su editor inteligente. Su contrato social consiste en proporcionar cierta información, al forzar (algunas de ellas) a requerir palabras clave, el orden en que se presentan se ha vuelto irrelevante.fuente
Actualizar:
Me di cuenta de que usarlo
**kwargs
no resolvería el problema. Si sus programadores cambian los argumentos de la función como lo deseen, uno podría, por ejemplo, cambiar la función a esto:y el código anterior se rompería nuevamente (porque ahora cada llamada a función debe incluir el primer argumento).
Realmente se reduce a lo que dice Bryan.
En general, al cambiar funciones, los argumentos nuevos siempre deben ir al final. De lo contrario, rompe el código. Debería ser obvio.
Si alguien cambia la función para que el código se rompa, este cambio debe rechazarse.
(Como dice Bryan, es como un contrato)
Al observar la firma de la función (es decir
def info(object, spacing=10, collapse=1)
), uno debería ver inmediatamente que todos los argumentos que no tienen un valor predeterminado son obligatorios.Para qué es el argumento, debe ir en la cadena de documentos.
Respuesta anterior (guardada para completar) :
Probablemente esta no sea una buena solución:Puede definir funciones de esta manera:
kwargs
es un diccionario que contiene cualquier argumento de palabra clave. Puede comprobar si hay un argumento obligatorio y, en caso contrario, generar una excepción.La desventaja es que puede que ya no sea tan obvio qué argumentos son posibles, pero con una cadena de documentos adecuada, debería estar bien.
fuente
Los argumentos de solo palabra clave de python3 (
*
) se pueden simular en python2.x con**kwargs
Considere el siguiente código de python3:
y su comportamiento:
Esto puede ser simulado utilizando la siguiente, tenga en cuenta que he tomado la libertad de cambiar
TypeError
aKeyError
en el caso "requerido llamado argumento", no sería demasiado trabajo para hacer que el mismo tipo de excepción, asíY comportamiento:
La receta funciona igualmente bien en python3.x, pero debe evitarse si solo es python3.x
fuente
kwargs.pop('foo')
es un modismo de Python 2? Necesito actualizar mi estilo de codificación. Todavía estaba usando este enfoque en Python 3 🤔Puede declarar que sus funciones
**args
solo reciben . Eso exigiría argumentos de palabras clave, pero tendría un trabajo adicional para asegurarse de que solo se pasen nombres válidos.fuente
foo(**kwargs)
. ¿Qué paso en eso?foo(killme=True, when="rightnowplease")
dict
.Como dicen otras respuestas, cambiar las firmas de funciones es una mala idea. Agregue nuevos parámetros al final o corrija todas las llamadas si se insertan argumentos.
Si aún desea hacerlo, use un decorador de funciones y la función inspect.getargspec . Se usaría algo como esto:
La implementación de
require_named_args
se deja como ejercicio para el lector.No me molestaría. Será lento cada vez que se llame a la función, y obtendrá mejores resultados escribiendo código con más cuidado.
fuente
Podrías usar el
**
operador:de esta manera la gente se ve obligada a utilizar parámetros con nombre.
fuente
en python si usa * args, eso significa que puede pasar n-número de argumentos para este parámetro, que vendrá como una lista dentro de la función para acceder
y si usa ** kw, eso significa sus argumentos de palabra clave, a los que se puede acceder como dict; puede pasar n-número de kw args, y si desea restringir, ese usuario debe ingresar la secuencia y los argumentos en orden, entonces no use * y ** - (su forma pitónica de proporcionar soluciones genéricas para grandes arquitecturas ...)
si desea restringir su función con valores predeterminados, puede verificar dentro de ella
fuente
No entiendo por qué un programador agregará un parámetro entre otros dos en primer lugar.
Si desea que los parámetros de la función se usen con nombres (p
info(spacing=15, object=odbchelper)
. Ej. ), Entonces no debería importar en qué orden se definen, por lo que también puede poner los nuevos parámetros al final.Si desea que el orden sea importante, ¡no puede esperar que nada funcione si lo cambia!
fuente