¿Cuál es la forma de evitar Pythonic parámetros por defecto que son listas vacías?

StackOverflow https://stackoverflow.com/questions/366422

  •  21-08-2019
  •  | 
  •  

Pregunta

A veces parece natural tener un parámetro por defecto que es una lista vacía. Sin embargo, Python da un comportamiento inesperado en estas situaciones .

Si, por ejemplo, tengo una función:

def my_func(working_list = []):
    working_list.append("a")
    print(working_list)

La primera vez que se llama el valor por defecto va a funcionar, pero las llamadas después de que se actualice la lista existente (con una "a" cada llamada) e imprimir la versión actualizada.

Así que, ¿cuál es la forma Pythonic para obtener el comportamiento que deseo (una nueva lista en cada llamada)?

¿Fue útil?

Solución

def my_func(working_list=None):
    if working_list is None: 
        working_list = []

    working_list.append("a")
    print(working_list)

Los documentos decir que usted debe utilizar None como predeterminado y para ello en el cuerpo de la función.

Otros consejos

respuestas existentes ya han proporcionado las soluciones directas como pedido. Sin embargo, ya que esta es una trampa muy común que los nuevos programadores de Python, que valga la pena añadir la explicación de por qué pitón se comporta de esta manera, que está muy bien resume en " la guía de los autostopistas a Python " como " mutables argumentos por defecto ": http://docs.python-guide.org/en/latest/writing/ trampas /

Cita:. " los argumentos por defecto de Python son evaluados una vez cuando se define la función, no cada vez que la función se llama (como lo es en, por ejemplo, Ruby) Esto significa que si se utiliza un argumento predeterminado mutable y mutar él, se le han mutado y ese objeto para todas las llamadas futuras a la función, así "

Código de ejemplo para ponerlo en práctica:

def foo(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to

No es que importe, en este caso, pero se puede utilizar la identidad del objeto para probar Ninguno:

if working_list is None: working_list = []

También puede tomar ventaja de la forma en que el operador booleano o se define en Python:

working_list = working_list or []

Aunque esto se comportará de forma inesperada si la persona que llama le da una lista vacía (que cuenta como falsa) como working_list y espera que su función para modificar la lista se la dio.

Si la intención de la función es la de Modificar el parámetro pasado como working_list, véase la respuesta de HenryR (= Ninguno, compruebe si hay ninguno en el interior).

Sin embargo, si no se tiene la intención de mutar el argumento, sólo lo utilizan como punto de partida para una lista, puede simplemente copiarlo:

def myFunc(starting_list = []):
    starting_list = list(starting_list)
    starting_list.append("a")
    print starting_list

(o en este caso sencillo simplemente print starting_list + ["a"] pero supongo que era sólo un ejemplo de juguete)

En general, la mutación de sus argumentos es mal estilo en Python. Las únicas funciones que están totalmente espera para mutar un objeto son métodos del objeto. Es aún más raro de mutar un argumento opcional - es un efecto secundario que ocurre sólo en algunas llamadas realmente la mejor interfaz

  • Si lo haces desde el hábito de "C" argumentos de salida, eso es totalmente innecesario. - siempre se puede devolver múltiples valores como una tupla

  • Si hace esto de manera eficiente para construir una larga lista de resultados sin necesidad de construir listas intermedias, que no escribe como un generador y utilizando result_list.extend(myFunc()) cuando se está llamando. De esta manera sus convenciones de llamada sigue siendo muy limpio.

Un patrón en el que la mutación de un arg opcional es con frecuencia es un hecho oculto arg "memo" en las funciones recursivas:

def depth_first_walk_graph(graph, node, _visited=None):
    if _visited is None:
        _visited = set()  # create memo once in top-level call

    if node in _visited:
        return
    _visited.add(node)
    for neighbour in graph[node]:
        depth_first_walk_graph(graph, neighbour, _visited)

Puede ser que sea fuera de tema, pero recuerda que si lo que desea es pasar un número variable de argumentos, la forma Pythonic es pasar una tupla *args o un diccionario **kargs. Estos son opcionales y son mejores que la sintaxis myFunc([1, 2, 3]).

Si desea pasar una tupla:

def myFunc(arg1, *args):
  print args
  w = []
  w += args
  print w
>>>myFunc(1, 2, 3, 4, 5, 6, 7)
(2, 3, 4, 5, 6, 7)
[2, 3, 4, 5, 6, 7]

Si desea pasar un diccionario:

def myFunc(arg1, **kargs):
   print kargs
>>>myFunc(1, option1=2, option2=3)
{'option2' : 2, 'option1' : 3}

Ya ha sido bueno y respuestas correctas proporcionado. Sólo quería dar otra sintaxis para escribir lo que quiere hacer lo que me parece más bello cuando, por ejemplo, desea crear una clase con listas vacías por defecto:

class Node(object):
    def __init__(self, _id, val, parents=None, children=None):
        self.id = _id
        self.val = val
        self.parents = parents if parents is not None else []
        self.children = children if children is not None else []

Este fragmento hace uso de la sintaxis del operador else if. Me gusta especialmente porque es un poco de una sola línea ordenada y sin signos de dos puntos, etc. involucrados y que casi se lee como una frase normal de Inglés. :)

En su caso se podría escribir

def myFunc(working_list=None):
    working_list = [] if working_list is None else working_list
    working_list.append("a")
    print working_list

Tomé la clase de extensión UCSC Python for programmer

cual es cierto de: def Fn (datos = []):

  

a) es una buena idea para que sus listas de datos comienzan vacío con cada llamada.

     

b) es una buena idea para que todas las llamadas a la función que no ofrecen ningún argumento en la llamada debe leer la lista vacía como de datos.

     

c) es una idea razonable, siempre y cuando sus datos es una lista de cadenas.

     

d) es una mala idea porque el valor por defecto [] se acumulará datos y el valor predeterminado [] cambiará con las llamadas posteriores.

Respuesta:

  

d) es una mala idea porque el valor por defecto [] se acumulará datos y el valor predeterminado [] cambiará con las llamadas posteriores.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top