python - barplot - Es el uso del del malo?
pandas plot (7)
Comúnmente uso del
en mi código para eliminar objetos:
>>> array = [4, 6, 7, ''hello'', 8]
>>> del(array[array.index(''hello'')])
>>> array
[4, 6, 7, 8]
>>>
Pero he escuchado a mucha gente decir que el uso de del
es antiponético. ¿Está usando del
mala práctica?
>>> array = [4, 6, 7, ''hello'', 8]
>>> array[array.index(''hello''):array.index(''hello'')+1] = ''''
>>> array
[4, 6, 7, 8]
>>>
Si no, ¿por qué hay muchas maneras de lograr lo mismo en Python? ¿Es uno mejor que los otros?
Opción 1: uso del
>>> arr = [5, 7, 2, 3]
>>> del(arr[1])
>>> arr
[5, 2, 3]
>>>
Opción 2: usar list.remove()
>>> arr = [5, 7, 2, 3]
>>> arr.remove(7)
>>> arr
[5, 2, 3]
>>>
Opción 3: usar list.pop()
>>> arr = [5, 7, 2, 3]
>>> arr.pop(1)
7
>>> arr
[5, 2, 3]
>>>
Opción 4: usar cortar
>>> arr = [5, 7, 2, 3]
>>> arr[1:2] = ''''
>>> arr
[5, 2, 3]
>>>
Lamento si esta pregunta parece estar basada en opiniones, pero estoy buscando una respuesta razonable a mi pregunta, y agregaré una recompensa después de 2 días si no obtengo una respuesta adecuada.
Editar:
Dado que hay muchas alternativas para usar del
para eliminar ciertas partes de objetos, el único factor que queda de del
es su capacidad para eliminar objetos por completo:
>>> a = ''hello''
>>> b = a
>>> del(a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''a'' is not defined
>>> b
''hello''
>>>
Sin embargo, ¿de qué sirve usarlo para ''indefinir'' objetos?
Además, ¿por qué el siguiente código cambia ambas variables?
>>> a = []
>>> b = a
>>> a.append(9)
>>> a
[9]
>>> b
[9]
>>>
Pero la sentencia del
no logra el mismo efecto?
>>> a = []
>>> b = a
>>> del(a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''a'' is not defined
>>> b
[]
>>>
Con respecto a la pregunta en tu "EDIT",
>>> a = []
>>> b = a
>>> a.append(9)
>>> a
[9]
>>> b
[9]
>>> del a
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''a'' is not defined
>>> b
[9]
>>>
esto se explica fácilmente, recuerde que:
>>> id(a) == id(b)
True
( a
y b
apuntan al mismo objeto en la memoria) y esa memoria en python es administrada por el GC. Al invocar del
en un objeto, simplemente disminuye su recuento de referencias en 1 (junto con eliminar el nombre del alcance), el objeto se destruye cuando el recuento de referencias llega a 0. En ese caso, b
aún contiene una referencia al objeto, por lo tanto, no se destruye y sigue siendo accesible.
Puedes encontrar más información here
El uso del del
mismo no es malo per se; sin embargo, tiene dos aspectos que contribuyen a olores de código particulares:
- Es un efecto secundario, parte de una secuencia de pasos, y no significativo por sí mismo.
- Puede ser que se produzca en el código que tiene una gestión manual de la memoria indicativa de una comprensión deficiente del alcance de Python y la gestión automática de la memoria. De la misma manera que el enunciado
with
es más idiomático para manejar manejadores de archivos quefile.close
, usar el alcance y el contexto es más idiomático que el uso manual de los miembros.
Pero esto es difícilmente canon: si la palabra clave del
era realmente "mala", no estaría en el núcleo del lenguaje. Solo estoy tratando de hacer de Devil''s Advocate, para explicar por qué algunos programadores pueden llamarlo "malo" y, posiblemente, darle una posición para argumentar en contra. ;)
Las otras respuestas lo están mirando desde un punto de vista técnico (es decir, cuál es la mejor manera de modificar una lista), pero diría que la (mucho) más importante razón que la gente sugiere, por ejemplo, cortar es que no modifica el original lista.
La razón de esto a su vez es que, por lo general, la lista vino de algún lado. Si lo modifica, puede causar efectos colaterales muy malos y difíciles de detectar, lo que puede causar errores en otras partes del programa. O incluso si no causa un error inmediatamente, hará que su programa en general sea más difícil de entender y razonar, y depurar.
Por ejemplo, la lista de comprensiones / expresiones del generador es agradable porque nunca cambian la lista de "origen" que se transmiten:
[x for x in lst if x != "foo"] # creates a new list
(x for x in lst if x != "foo") # creates a lazy filtered stream
Esto es, por supuesto, a menudo más caro (en cuanto a la memoria) porque crea una nueva lista, pero un programa que utiliza este enfoque es matemáticamente más puro y más fácil de razonar. Y con listas diferidas (generadores y expresiones de generador), incluso la sobrecarga de la memoria desaparecerá, y los cálculos solo se ejecutarán a pedido; ver http://www.dabeaz.com/generators/ para una introducción impresionante. Y no debe pensar demasiado en la optimización al diseñar su programa (consulte https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil ). Además, eliminar un elemento de una lista es bastante caro, a menos que sea una lista vinculada (que la list
de Python no es, para la lista vinculada, vea collections.deque
).
De hecho, las funciones libres de efectos secundarios y las estructuras de datos immutable son la base de la Programación Funcional , un paradigma de programación muy poderoso.
Sin embargo, bajo ciertas circunstancias, está bien modificar una estructura de datos en su lugar (incluso en FP, si el lenguaje lo permite ), como cuando se crea localmente, o se copia de la entrada de la función:
def sorted(lst):
ret = list(lst) # make a copy
# mutate ret
return ret
- esta función parece ser una función pura desde el exterior porque no modifica sus entradas (y también solo depende de sus argumentos y nada más (es decir, no tiene un estado (global)), que es otro requisito para que algo sea una Function Pure ).
Así que mientras sepas lo que estás haciendo, del
no es malo en absoluto; pero use cualquier tipo de mutación de datos con extremo cuidado y solo cuando sea necesario. Comience siempre con un código posiblemente menos eficiente pero más correcto y matemáticamente elegante.
... y aprende la programación funcional :)
PD nota que del
también se puede utilizar para eliminar variables locales y eliminar así las referencias a los objetos en la memoria, que a menudo es útil para cualquier propósito relacionado con GC.
Responde a tu segunda pregunta:
En cuanto a la segunda parte de su pregunta sobre la eliminación total de objetos , ese no es el caso: de hecho, en Python, ni siquiera es posible decirle al intérprete / VM que elimine un objeto de la memoria porque Python es un lenguaje basura (como Java, C #, Ruby, Haskell, etc.) y es el tiempo de ejecución el que decide qué eliminar y cuándo.
En cambio, qué hace el del
cuando se invoca una variable (a diferencia de una clave del diccionario o un elemento de la lista) como esta:
del a
es que solo elimina la variable local (o global) y no a lo que apunta la variable (cada variable en Python contiene un puntero / referencia a su contenido, no el contenido mismo). De hecho, como los lugareños y los globales se almacenan como un diccionario bajo el capó (ver locals()
y globals()
), del a
es equivalente a:
del locals()[''a'']
o del globals()[''a'']
cuando se aplica a un global.
entonces si tienes:
a = []
b = a
está haciendo una lista, almacenando una referencia a ella en a
y luego haciendo otra copia de esa referencia y almacenándola en b
sin copiar / tocar el objeto de la lista. Por lo tanto, estas dos llamadas afectan a uno y el mismo objeto:
a.append(1)
b.append(2)
# the list will be [1, 2]
mientras que borrar b
está de ninguna manera relacionado con tocar lo que b
apunta a:
a = []
b = a
del b
# a is still untouched and points to a list
Además, incluso cuando llamas a del
en un atributo de objeto (por ejemplo, del self.a
), en realidad estás modificando un diccionario self.__dict__
como si estuvieras modificando locals()
/ globals()
cuando haces del a
.
PD como Sven Marcnah ha señalado que del locals()[''a'']
realidad no elimina la variable local a
cuando está dentro de una función, que es correcta. Esto probablemente se deba a que los locals()
devuelven una copia de los lugareños reales. Sin embargo, la respuesta sigue siendo válida en general.
No creo que haya escuchado a nadie decir que del
es malvado, al menos no más que cualquier otra característica del lenguaje. La pregunta entre del
y otros enfoques realmente se reduce a sus casos de uso. Los siguientes casos son geniales para del
:
Eliminando variables de su alcance actual. Por qué querrías hacer esto? Imagine que está declarando un módulo que calcula una variable de paquete, pero que los consumidores de ese módulo nunca lo necesitan. Si bien podría crear un módulo completamente nuevo para él, podría ser excesivo o podría oscurecer lo que realmente se calcula. Por ejemplo, es posible que desee lo siguiente:
GLOBAL_1 = ''Some arbitrary thing'' GLOBAL_2 = ''Something else'' def myGlobal3CalculationFunction(str1, str2): # Do some transforms that consumers of this module don''t need return val GLOBAL_3 = myGlobal3CalculationFunction(GLOBAL_1, GLOBAL_2) # Mystery function exits stage left del myGlobal3CalculationFunction
Básicamente, nadie está en desacuerdo con el uso de
del
para eliminar variables del alcance, cuando sea necesario. Lo mismo se aplica a los valores en los diccionarios, o casi cualquier cosa accedida por nombre o referencias inmutables similares (propiedades de clase, propiedades de instancia, valores dict, etc.).El otro caso es donde desea eliminar un elemento de una lista o secuencia ordenada similar. Lo que en realidad no es tan diferente del primer caso en algunos aspectos (dado que se puede acceder a todos ellos como contenedores de clave-valor, con listas que simplemente tienen claves enteras ordenadas de manera confiable). En todos estos casos, está en el mismo barco de querer eliminar una referencia a algunos datos que existen en esa instancia particular (ya que incluso las clases son una instancia de ser una clase). Estás haciendo una modificación en el lugar.
¿Tener índices ordenados y especiales significa que cualquier cosa es diferente para las listas? La diferencia fundamental con una lista es que hacer una modificación in situ hace que todas tus antiguas claves sean básicamente inútiles, a menos que seas muy cuidadoso. Python te da la gran capacidad de representar datos de forma muy semántica: en lugar de tener una lista de
[actor, verb, object]
e índices de mapeo, puedes tener un buen dict de{''actor'' : actor, ''verb'' : verb, ''object'' : object}
. A menudo hay un gran valor en ese tipo de acceso (por eso accedemos a las funciones por nombre, en lugar de por número): si el orden no es importante, ¿por qué hacerlo rígido? Si su pedido ES IMPORTANTE, ¿por qué está arruinando algo? Esto invalida todas sus referencias (es decir, posiciones de los elementos, distancia entre los elementos).
El problema se reduce a por qué estarías eliminando directamente un valor de lista por índice. En la mayoría de los casos, las operaciones que modifican elementos individuales de listas en el lugar tienen implementaciones obvias a través de otras funciones. ¿Matar un artículo con un valor dado? Lo remove
¿Implementando una cola o una pila? Lo pop
(no lo bloquee). ¿Reducir el conteo de ref para una instancia en la lista? l[i] = None
funciona igual de bien, y sus índices anteriores todavía apuntan a las mismas cosas. ¿Filtrando elementos? filter
o usa una lista de comprensión. Hacer una copia de la lista, menos algunos elementos? Usted lo slice
. Deshacerse de elementos duplicables y lavables? Puede list(set([]))
o mirar itertools
si solo necesita atravesar los elementos únicos una vez.
Después de deshacerse de todos esos casos, terminas con dos casos de uso comunes para usar del
para una lista. Primero, puede eliminar elementos aleatorios por índice. Hay más de unos pocos casos donde esto podría ser útil, y del
es totalmente apropiado. En segundo lugar, ha almacenado índices que representan dónde se encuentra en la lista (es decir, caminando de una habitación a otra en un pasillo donde a veces destruye una habitación al azar, de la Guía de estilo de programación de Charlie Sheen). Esto se vuelve difícil si tiene más de un índice para la misma lista, ya que usar del
significa que todos los índices deben ajustarse en consecuencia. Esto es menos común, ya que las estructuras que recorre usando índices a menudo no son de las que elimina elementos (por ejemplo, cuadrículas de coordenadas para un tablero de juego). Sucede, sin embargo, como al pasar el tiempo por una lista para sondear trabajos y eliminar los que se han completado.
Esto indica el problema fundamental con la eliminación de elementos de una lista in situ por índice: estás bastante atrapado haciéndolo uno a la vez. Si tiene los índices de dos elementos para eliminar, ¿luego elimine el primero? Existe una buena posibilidad de que su índice anterior no apunte a lo que solía. Las listas son para almacenar el orden. Como del
altera el orden absoluto, estás atrapado caminando o saltando a lo largo de la lista. Una vez más, hay casos de uso sólidos (por ejemplo, destrucción aleatoria), pero hay toneladas de otros casos que simplemente están mal. Particularmente entre los nuevos programadores de Python, la gente hace cosas horribles con bucles while en funciones (es decir, bucle hasta que encuentre un valor que coincida con una entrada, del
índice). Del
requiere un índice como entrada, y tan pronto como se ejecuta, hace que todos los índices existentes que se refieren a esa lista se refieran a datos completamente diferentes. Puedes ver dónde está esa pesadilla de mantenimiento si se mantienen índices múltiples. Nuevamente, no está mal. Es solo que rara vez en la práctica es la mejor manera de hacer cosas con una lista en Python.
No, no creo que usar del
sea sea malo en absoluto. De hecho, hay situaciones en las que es esencialmente la única opción razonable, como eliminar elementos de un diccionario:
k = {''foo'': 1, ''bar'': 2}
del k[''foo'']
Tal vez el problema es que los principiantes no entienden completamente cómo funcionan las variables en Python, por lo que el uso (o mal uso) de del
puede ser desconocido.
Python simplemente contiene muchas formas diferentes de eliminar elementos de una lista. Todos son útiles en diferentes situaciones.
# removes the first index of a list
del arr[0]
# Removes the first element containing integer 8 from a list
arr.remove(8)
# removes index 3 and returns the previous value at index 3
arr.pop(3)
# removes indexes 2 to 10
del arr[2:10]
Por lo tanto, todos tienen su lugar. Claramente, cuando se quiere eliminar el número 8, el ejemplo número 2 es una mejor opción que 1 o 3. Por lo tanto, es realmente lo que tiene sentido según las circunstancias y lo que es más lógico.
EDITAR
La diferencia entre arr.pop (3) y del arr [3] es que pop devuelve el elemento eliminado. Por lo tanto, puede ser útil para transferir elementos eliminados a otras matrices o estructuras de datos. De lo contrario, los dos no difieren en el uso.
del
solo muta la variable, que a veces es innecesaria. Por lo tanto, sus soluciones anteriores pueden ser mejores. Sin embargo, del
es la única forma de ''destruir'' variables, y eliminarlas para siempre:
>>> a = 9
>>> del(a)
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ''a'' is not defined
>>>
Además, puedes eliminar elementos de los diccionarios:
>>> dict = {1: 6}
>>> dict[1]
6
>>> del(dict[1])
>>> dict
{}
>>>