¿Hay una manera de establecer un parámetro predeterminado igual a otro valor de parámetro?

Por ejemplo, tengo un método básico que devolverá una lista de permutaciones.

import itertools def perms(elements, set_length=elements): data=[] for x in range(elements): data.append(x+1) return list(itertools.permutations(data, set_length)) 

Ahora entiendo, que en su estado actual este código no se ejecutará porque los segundos elements no están definidos, pero ¿hay una manera elegante de lograr lo que estoy tratando de hacer aquí? Si eso todavía no está claro, quiero hacer que el valor predeterminado de setLength sea ​​igual al primer argumento pasado. Gracias.

No, los valores predeterminados de los parámetros de palabra clave de función se determinan cuando se define la función, no cuando se ejecuta la función.

Establezca el valor predeterminado en None y detecte que:

 def perms(elements, setLength=None): if setLength is None: setLength = elements 

Si necesita poder especificar None como argumento, use un valor de centinela diferente:

 _sentinel = object() def perms(elements, setLength=_sentinel): if setLength is _sentinel: setLength = elements 

Ahora las personas que llaman pueden establecer setLength en None y no se verá como el valor predeterminado.

Debido a la forma en que Python maneja los enlaces y los parámetros predeterminados …

La forma estándar es:

 def perms(elements, setLength=None): if setLength is None: setLength = elements 

Y otra opción es:

 def perms(elements, **kwargs): setLength = kwargs.pop('setLength', elements) 

Aunque esto requiere que use explícitamente los perms(elements, setLength='something else') si no quiere un valor predeterminado …

Deberías hacer algo como:

 def perms(elements,setLength=None): if setLength is None: setLength = elements 

Respuesta 1:

La solución de arriba se ve así:

 def cast_to_string_concat(a, b, c=None): c = a if c is None else c return str(a) + str(b) + str(c) 

¡Si bien este enfoque resolverá una gran cantidad de problemas potenciales (y quizás el suyo)! Quería escribir una función en la que una entrada posible para la variable ” c ” fuera el Singleton None , así que tuve que hacer más excavaciones.

Para explicarlo más, llamando a la función con las siguientes variables:

 A='A' B='B' my_var = None 

Rendimientos:

 cast_to_string_concat(A, B, my_var): >>>'ABA' 

Mientras que el usuario podría esperar eso, ya que llamaron a la función con tres variables, debería imprimir las tres variables, así:

 cast_to_string_concat(A, B, my_var): >>> 'ABNone' # simulated and expected outcome 

Entonces, esta implementación ignora la tercera variable, incluso cuando fue declarada, por lo que esto significa que la función ya no tiene la capacidad de determinar si la variable ” c ” fue o no definida.

Por lo tanto, para mi caso de uso, un valor predeterminado de None no serviría para nada.

Para las respuestas que sugieren esta solución, lea estos:

  • ¿Hay una manera de establecer un parámetro predeterminado igual a otro valor de parámetro?
  • El método abreviado de Python para que el valor predeterminado de la variable sea otro valor de variable si es Ninguno
  • Valor predeterminado de la función argumento igual a otro argumento
  • ¿Cuál es la forma pythonica de evitar los parámetros predeterminados que son listas vacías?

Pero, si eso no funciona para usted, entonces tal vez siga leyendo.


Un comentario en el primer enlace anterior menciona el uso de un _sentinel definido por object() .

Por lo tanto, esta solución elimina el uso de Ninguno y lo reemplaza con el object() mediante el uso del sentinel privado implícito .


Respuesta 2:

 _sentinel = object() def cast_to_string_concat(a, b, c=_sentinel): c = a if c == _sentinel else c return str(a) + str(b) + str(c) 
 A='A' B='B' C='C' cast_to_string_append(A,B,C) >>> 'ABC' cast_to_string_concat(A,B) >>> 'ABA' 

¡Así que esto es bastante impresionante! ¡Maneja correctamente la caja del borde anterior! Ver por ti mismo:


 A='A' B='B' C = None cast_to_string_concat(A, B, C) >>> 'ABNone' 

Así que, hemos terminado, ¿verdad? ¿Hay alguna forma plausible de que esto no funcione? Hmm … probablemente no! Pero sí dije que esta era una respuesta en tres partes, ¡así que adelante! 😉


En aras de la exhaustividad, imaginemos que nuestro progtwig funciona en un espacio en el que todo escenario posible es posible. (Esto puede no ser una suposición justificada, pero me imagino que uno podría obtener el valor de _sentinel con suficiente información sobre la architecture de la computadora y la implementación de la elección del objeto. Entonces, si está dispuesto, supongamos que efectivamente es posible, e imaginemos que decidimos probar esa hipótesis que hace referencia a _sentinel como se definió anteriormente.


 _sentinel = object() def cast_to_string_concat(a, b, c=_sentinel): c = a if c == _sentinel else c return str(a) + str(b) + str(c) 
 A='A' B='B' S = _sentinel cast_to_string_append(A,B,S) >>> 'ABA' 

¡Espera un minuto! Ingresé tres argumentos, ¡así que debería ver la concatenación de cuerdas de los tres juntos!


* Cola entrando en la tierra de consecuencias imprevistas *

Quiero decir, en realidad no. Una respuesta de: “Eso es un territorio de caso de borde insignificante !!” o su ilk está perfectamente garantizado.

Y ese sentimiento es correcto! ¡En este caso (y probablemente en la mayoría de los casos) no vale la pena preocuparse por esto!

Pero si vale la pena preocuparse, o si solo desea la satisfacción matemática de eliminar todos los casos de vanguardia de los que tiene conocimiento … ¡adelante!

Ejercicio dejado al lector:

Al desviarse de esta técnica, puede afirmar directamente c=object() , sin embargo, honestamente, no he conseguido que esa manera trabaje para mí. Mi investigación muestra que c == object() es False , y str(c) == str(object()) también es False , y es por eso que estoy usando la implementación de Martin Pieters .


Bien, después de ese largo ejercicio, estamos de vuelta!

Recuerde que el objective es escribir una función que podría tener n entradas, y solo cuando no se proporciona una variable, luego copiará otra variable en la posición i .

En lugar de definir la variable por defecto, ¿qué pasa si cambiamos el enfoque para permitir un número arbitrario de variables?

Entonces, si está buscando una solución que no comprometa las entradas potenciales, donde una entrada válida podría ser None , object() o _sentinel … entonces (y solo entonces), en este punto, estoy pensando Mi solución será útil. La inspiración para la técnica vino de la segunda parte de la respuesta de Jon Clements .


Respuesta 3:

Mi solución a este problema es cambiar la denominación de esta función y envolver esta función con una función de la convención de denominación anterior, pero en lugar de utilizar variables, usamos *args . A continuación, define la función original dentro del ámbito local (con el nuevo nombre) y solo permite las pocas posibilidades que desee.

En pasos:

  1. Cambiar el nombre de la función a algo similar
  2. Elimine la configuración predeterminada para su parámetro opcional
  3. Comience a crear una nueva función justo arriba y tabule la función original en.
  def cast_to_string_concat(*args): 
  1. Determine la aridad de su función – (Encontré esa palabra en mi búsqueda … ese es el número de los parámetros pasados ​​a una función dada)
  2. Utilice una statement de caso en el interior que determine si ingresó un número válido de variables, y realice el ajuste correspondiente.
 def cast_to_string_append(*args): def string_append(a, b, c): # this is the original function, it is only called within the wrapper return str(a) + str(b) + str(c) if len(args) == 2: # if two arguments, then set the third to be the first return string_append(*args, args[0]) elif len(args) == 3: # if three arguments, then call the function as written return string_append(*args) else: raise Exception(f'Function: cast_to_string_append() accepts two or three arguments, and you entered {len(args)}.') 
 # instantiation A='A' B='B' C='C' D='D' _sentinel = object() S = _sentinel N = None 
 """ Answer 3 Testing """ # two variables cast_to_string_append(A,B) >>> 'ABA' # three variables cast_to_string_append(A,B,C) >>> 'ABC' # three variables, one is _sentinel cast_to_string_append(A,B,S) >>>'AB' # three variables, one is None cast_to_string_append(A,B,N) >>>'ABNone' # one variable cast_to_string_append(A) >>>Traceback (most recent call last): >>> File "", line 1, in  >>> File "", line 13, in cast_to_string_append >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 1. # four variables cast_to_string_append(A,B,C,D) >>>Traceback (most recent call last): >>> File "", line 1, in  >>> File "", line 13, in cast_to_string_append >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 4. # ten variables cast_to_string_append(0,1,2,3,4,5,6,7,8,9) >>>Traceback (most recent call last): >>> File "", line 1, in  >>> File "", line 13, in cast_to_string_append >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 10. # no variables cast_to_string_append() >>>Traceback (most recent call last): >>> File "", line 1, in  >>> File "", line 13, in cast_to_string_append >>>Exception: Function: cast_to_string_append() accepts two or three arguments, and you entered 0. """ End Answer 3 Testing """ 

Entonces, en resumen:

  • Respuesta 1 : la respuesta más simple, y funciona en la mayoría de los casos.
 def cast_to_string_concat(a, b, c=None): c = a if c is None else c return str(a) + str(b) + str(c) 
  • Respuesta 2 : use si None no significa realmente un parámetro vacío cambiando a object() , a través de _sentinel .
 _sentinel = object() def cast_to_string_concat(a, b, c=_sentinel): c = a if c == _sentinel else c return str(a) + str(b) + str(c) 
  • La respuesta 3 busca una solución general que utiliza una función de envoltorio con aridad arbitraria utilizando *args , y maneja los casos aceptables dentro de:
 def cast_to_string_append(*args): def string_append(a, b, c): # this is the original function, it is only called within the wrapper return str(a) + str(b) + str(c) if len(args) == 2: # if two arguments, then set the third to be the first return string_append(*args, args[0]) elif len(args) == 3: # if three arguments, then call the function as written return string_append(*args) else: raise Exception(f'Function: cast_to_string_append() accepts two or three arguments, and you entered {len(args)}.') 

¡Usa lo que funcione para ti! Pero para mí, estaré usando la Opción 3;)