¿Cuál es la diferencia entre los métodos de lista de Python para agregar y extender?
¿ Cuál es la diferencia entre los métodos de lista append()
y extend()
?
.append()
agrega un objeto específico al final de la lista:
>>> x = [1, 2, 3]
>>> x.append([4, 5])
>>> print(x)
[1, 2, 3, [4, 5]]
.extend()
extiende la lista agregando elementos del iterable especificado:
>>> x = [1, 2, 3]
>>> x.extend([4, 5])
>>> print(x)
[1, 2, 3, 4, 5]
.append()
agrega un elemento a una lista,
mientras que .extend()
concatena la primera lista con otra lista/iterable.
>>> xs = ['A', 'B']
>>> xs
['A', 'B']
>>> xs.append("D")
>>> xs
['A', 'B', 'D']
>>> xs.append(["E", "F"])
>>> xs
['A', 'B', 'D', ['E', 'F']]
>>> xs.insert(2, "C")
>>> xs
['A', 'B', 'C', 'D', ['E', 'F']]
>>> xs.extend(["G", "H"])
>>> xs
['A', 'B', 'C', 'D', ['E', 'F'], 'G', 'H']
¿Cuál es la diferencia entre los métodos de lista agregar y extender?
.append()
agrega su argumento como un solo elemento al final de una lista. La longitud de la lista aumentará en uno..extend()
itera sobre su argumento agregando cada elemento a la lista, extendiendo la lista. La longitud de la lista aumentará según la cantidad de elementos que haya en el argumento iterable.
.append()
El .append()
método agrega un objeto al final de la lista.
my_list.append(object)
Cualquiera que sea el objeto, ya sea un número, una cadena, otra lista u otra cosa, se agrega al final my_list
como una única entrada en la lista.
>>> my_list
['foo', 'bar']
>>> my_list.append('baz')
>>> my_list
['foo', 'bar', 'baz']
Tenga en cuenta que una lista es un objeto. Si agrega otra lista a una lista, la primera lista será un único objeto al final de la lista (que puede no ser lo que desea):
>>> another_list = [1, 2, 3]
>>> my_list.append(another_list)
>>> my_list
['foo', 'bar', 'baz', [1, 2, 3]]
#^^^^^^^^^--- single item at the end of the list.
.extend()
El .extend()
método extiende una lista agregando elementos de un iterable:
my_list.extend(iterable)
Entonces, con extender, cada elemento del iterable se agrega a la lista. Por ejemplo:
>>> my_list
['foo', 'bar']
>>> another_list = [1, 2, 3]
>>> my_list.extend(another_list)
>>> my_list
['foo', 'bar', 1, 2, 3]
Tenga en cuenta que una cadena es iterable, por lo que si extiende una lista con una cadena, agregará cada carácter a medida que itera sobre la cadena (que puede no ser lo que desea):
>>> my_list.extend('baz')
>>> my_list
['foo', 'bar', 1, 2, 3, 'b', 'a', 'z']
Sobrecarga del operador, __add__
( +
) y __iadd__
( +=
)
Ambos operadores +
y +=
están definidos para list
. Son semánticamente similares a extender.
my_list + another_list
crea una tercera lista en la memoria, por lo que puede devolver el resultado de la misma, pero requiere que el segundo iterable sea una lista.
my_list += another_list
modifica la lista in situ (es el operador in situ y las listas son objetos mutables, como hemos visto) para que no cree una lista nueva. También funciona como extender, en el sentido de que el segundo iterable puede ser cualquier tipo de iterable.
No se confunda, my_list = my_list + another_list
no es equivalente a +=
: le proporciona una lista nueva asignada a my_list.
Complejidad del tiempo
Append tiene una complejidad de tiempo constante ( amortizada ) , O(1).
Extender tiene complejidad temporal, O (k).
Iterar a través de las múltiples llamadas a .append()
aumenta la complejidad, lo que lo hace equivalente a extender, y dado que la iteración de extend se implementa en C, siempre será más rápido si tiene la intención de agregar elementos sucesivos de un iterable a una lista.
Con respecto a "amortizado" - de la lista de fuentes de implementación de objetos :
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
Esto significa que obtenemos los beneficios de una reasignación de memoria mayor de la necesaria por adelantado, pero podemos pagarla en la siguiente reasignación marginal con una aún mayor. El tiempo total para todos los anexos es lineal en O (n), y el tiempo asignado por anexo se convierte en O (1).
Actuación
Quizás se pregunte qué es más eficaz, ya que append se puede utilizar para lograr el mismo resultado que extender. Las siguientes funciones hacen lo mismo:
def append(alist, iterable):
for item in iterable:
alist.append(item)
def extend(alist, iterable):
alist.extend(iterable)
Así que cronometrémoslos:
import timeit
>>> min(timeit.repeat(lambda: append([], "abcdefghijklmnopqrstuvwxyz")))
2.867846965789795
>>> min(timeit.repeat(lambda: extend([], "abcdefghijklmnopqrstuvwxyz")))
0.8060121536254883
Abordar un comentario sobre horarios
Un comentarista dijo:
Respuesta perfecta, solo extraño el momento de comparar agregando solo un elemento
Haz lo semánticamente correcto. Si desea agregar todos los elementos en un iterable, use .extend()
. Si solo estás agregando un elemento, usa .append()
.
Bien, entonces creemos un experimento para ver cómo funciona esto con el tiempo:
def append_one(a_list, element):
a_list.append(element)
def extend_one(a_list, element):
"""creating a new list is semantically the most direct
way to create an iterable to give to extend"""
a_list.extend([element])
import timeit
Y vemos que hacer todo lo posible para crear un iterable solo para usar extender es una (menor) pérdida de tiempo:
>>> min(timeit.repeat(lambda: append_one([], 0)))
0.2082819009956438
>>> min(timeit.repeat(lambda: extend_one([], 0)))
0.2397019260097295
De esto aprendemos que no se gana nada al usar .extend()
cuando solo tenemos un elemento para agregar.
Además, estos tiempos no son tan importantes. Solo los muestro para dejar claro que, en Python, hacer lo semánticamente correcto es hacer las cosas de la manera correcta ™.
Es posible que pueda probar tiempos en dos operaciones comparables y obtener un resultado ambiguo o inverso. Solo concéntrate en hacer lo semánticamente correcto.
Conclusión
Vemos que .extend()
es semánticamente más claro y que puede ejecutarse mucho más rápido que .append()
cuando pretendes agregar cada elemento en un iterable a una lista.
Si solo tiene un elemento (no en un iterable) para agregar a la lista, use .append()
.
append
añade un solo elemento. extend
añade una lista de elementos.
Tenga en cuenta que si pasa una lista para agregar, aún agrega un elemento:
>>> a = [1, 2, 3]
>>> a.append([4, 5, 6])
>>> a
[1, 2, 3, [4, 5, 6]]