When is del useful in Python?
Firstly, you can del other things besides local variables
del list_item[4]
del dictionary["alpha"]
Both of which should be clearly useful. Secondly, using del
on a local variable makes the intent clearer. Compare:
del foo
to
foo = None
I know in the case of del foo
that the intent is to remove the variable from scope. It's not clear that foo = None
is doing that. If somebody just assigned foo = None
I might think it was dead code. But I instantly know what somebody who codes del foo
was trying to do.
When am I supposed to use del in python?
Never, unless you are very tight on memory and doing something very bulky. If you are writing usual program, garbage collector should take care of everything.
If you are writing something bulky, you should know that del
does not delete the object, it just dereferences it. I.e. variable no longer refers to the place in memory where object data is stored. After that it still needs to be cleaned up by garbage collector in order for memory to be freed (that happens automatically).
There is also a way to force garbage collector to clean objects - gc.collect()
, which may be useful after you ran del
. For example:
import gc
a = [i for i in range(1, 10 ** 9)]
...
del a
# Object [0, 1, 2, ..., 10 ** 9 - 1] is not reachable but still in memory
gc.collect()
# Object deleted from memory
Update: really good note in comments. Watch for other references to the object in memory. For example:
import gc
a = [i for i in range(1, 10 ** 9)]
b = a
...
del a
gc.collect()
After execution of this block, the large array is still reachable through b
and will not be cleaned.
Is the use of del bad?
The other answers are looking at it from a technical point of view (i.e. what's the best way to modify a list), but I would say the (much) more important reason people recommend, for example, slicing, is that it doesn't modify the original list.
The reason for this in turn is that usually, the list came from somewhere. If you modify it, you can unknowningly cause serious and hard-to-detect side effects, which can cause bugs elsewhere in the program. Or even if you don't cause a bug immediately, you'll make your program overall harder to understand and reason about, and debug.
For example, list comprehensions/generator expressions are nice in that they never mutate the "source" list they are passed:
[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
This is of course often more expensive (memory wise) because it creates a new list but a program that uses this approach is mathematically purer and easier to reason about. And with lazy lists (generators and generator expressions), even the memory overhead will disappear, and computations are only executed on demand; see http://www.dabeaz.com/generators/ for an awesome introduction. And you should not think too much about optimization when designing your program (see https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil). Also, removing an item from a list is quite expensive, unless it's a linked list (which Python's list
isn't; for linked list, see collections.deque
).
In fact, side-effect free functions and immutable data structures are the basis of Functional Programming, a very powerful programming paradigm.
However, under certain circumstances, it's OK to modify a data structure in place (even in FP, if the language allows it), such as when it's a locally created one, or copied from the function's input:
def sorted(lst):
ret = list(lst) # make a copy
# mutate ret
return ret
— this function appears to be a pure function from the outside because it doesn't modify its inputs (and also only depends on its arguments and nothing else (i.e. it has no (global) state), which is another requirement for something to be a Pure Function).
So as long as you know what you're doing, del
is by no means bad; but use any sort of data mutation with extreme care and only when you have to. Always start out with a possibly less efficient but more correct and mathematically elegant code.
...and learn Functional Programming :)
P.S. note that del
can also be used to delete local variables and thus eliminate references to objects in memory, which is often useful for whatever GC related purposes.
Answer to your second question:
As to the second part of your question about del
removing objects completely — that's not the case: in fact in Python, it is not even possible to tell the interpreter/VM to remove an object from memory because Python is a garbage collected language (like Java, C#, Ruby, Haskell etc) and it's the runtime that decides what to remove and when.
Instead, what del
does when called on a variable (as opposed to a dictionary key or list item) like this:
del a
is that it only removes the local (or global) variable and not what the variable points to (every variable in Python holds a pointer/reference to its contents not the content itself). In fact, since locals and globals are stored as a dictionary under the hood (see locals()
and globals()
), del a
is equivalent to:
del locals()['a']
or del globals()['a']
when applied to a global.
so if you have:
a = []
b = a
you're making a list, storing a reference to it in a
and then making another copy of that reference and storing it into b
without copying/touching the list object itself. Therefore, these two calls affect one and the same object:
a.append(1)
b.append(2)
# the list will be [1, 2]
whereas deleting b
is in no way related to touching what b
points to:
a = []
b = a
del b
# a is still untouched and points to a list
Also, even when you call del
on an object attribute (e.g. del self.a
), you're still actually modifying a dictionary self.__dict__
just like you are actually modifying locals()
/globals()
when you do del a
.
P.S. as Sven Marcnah has pointed out that del locals()['a']
does not actually delete the local variable a
when inside a function, which is correct. This is probably due to locals()
returning a copy of the actual locals. However, the answer is still generally valid.
Python using a del keyword inside a function
Python is a garbage-collected language so you don't need to delete anything (except for the very rare cases); If you have an object in memory, e.g. Game
in this case and assigned the reference to it to a variable in this case cup
it will reside in memory as long as the cup
is in scope (in this example the entire program lifetime). But if you assign something else to the cup
variable (anything, e.g. cup = None
) there's nothing referencing the Game
object so it will be scheduled for garbage collection.
In Python, is use of `del` statement a code smell?
I don't think that del
by itself is a code smell.
Reusing a variable name in the same namespace is definitely a code smell as is not using classes and other namespaces where appropriate. So using del
to facilitate that sort of thing is a code smell.
The only really appropriate use of del
that I can think of off the top of my head is breaking cyclic references which are often a code smell as well (and often times, this isn't even necessary). Remember, all del
does is delete the reference to the object and not the object itself. That will be taken care of by either reference counting or garbage collecting.
>>> a = [1, 2]
>>> b = a
>>> del a
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> b
[1, 2]
You can see that the list is kept alive after the del
statement because b
still holds a reference to it.
So, while del
isn't really a code smell, it can be associated with things that are.
What does del do exactly?
Python is a garbage-collected language. If a value isn't "reachable" from your code anymore, it will eventually get deleted.
The del
statement, as you saw, removes the binding of your variable. Variables aren't values, they're just names for values.
If that variable was the only reference to the value anywhere, the value will eventually get deleted. In CPython in particular, the garbage collector is built on top of reference counting. So, that "eventually" means "immediately".* In other implementations, it's usually "pretty soon".
If there were other references to the same value, however, just removing one of those references (whether by del x
, x = None
, exiting the scope where x
existed, etc.) doesn't clean anything up.**
There's another issue here. I don't know what the memory_profiler
module (presumably this one) actually measures, but the description (talking about use of psutil
) sounds like it's measuring your memory usage from "outside".
When Python frees up storage, it doesn't always—or even usually—return it to the operating system. It keeps "free lists" around at multiple levels so it can re-use the memory more quickly than if it had to go all the way back to the OS to ask for more. On modern systems, this is rarely a problem—if you need the storage again, it's good that you had it; if you don't, it'll get paged out as soon as someone else needs it and never get paged back in, so there's little harm.
(On top of that, which I referred to as "the OS" above is really an abstraction made up of multiple levels, from the malloc
library through the core C library to the kernel/pager, and at least one of those levels usually has its own free lists.)
If you want to trace memory use from the inside perspective… well, that's pretty hard. It gets a lot easier in Python 3.4 thanks to the new tracemalloc
module. There are various third-party modules (e.g., heapy
/guppy
, Pympler
, meliae
) that try to get the same kind of information with earlier versions, but it's difficult, because getting information from the various allocators, and tying that information to the garbage collector, was very hard before PEP 445.
* In some cases, there are references to the value… but only from other references that are themselves unreachable, possibly in a cycle. That still counts as "unreachable" as far as the garbage collector is concerned, but not as far as reference counts are concerned. So, CPython also has a "cycle detector" that runs every so often and finds cycles of mutually-reachable but not-reachable-from-anyone-else values and cleans them up.
** If you're testing in the interactive console, there may be hidden references to your values that are hard to track, so you might think you've gotten rid of the last reference when you haven't. In a script, it should always be possible, if not easy, to figure things out. The gc
module can help, as can the debugger. But of course both of them also give you new ways to add additional hidden references.
Why is del an instruction and not a method in python?
Because del
is a statement that you can delete several things with it, and since when you want to delete list_name[index]
with del
actually you want to delete an object and this is the job that del
does for other objects so there is no need to create an redundant attribute for lists to does that!
Deletion is recursively defined very similar to the way assignment is defined. Rather than spelling it out in full details, here are some hints.
Deletion of a target list recursively deletes each target, from left to right.
Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a global statement in the same code block. If the name is unbound, a NameError exception will be raised.
Deletion of attribute references, subscriptions and slicings is passed to the primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object).
Related Topics
Create an Empty List with Certain Size in Python
How to Filter Foreignkey Choices in a Django Modelform
What's the Difference Between Globals(), Locals(), and Vars()
How to Find All the Subclasses of a Class Given Its Name
Extract First Item of Each Sublist
How to Add Group Labels for Bar Charts in Matplotlib
Replace Values in a Pandas Series via Dictionary Efficiently
How to Include a Folder with Cx_Freeze
What's the Difference Between a Python Module and a Python Package
How to Open Multiple Files Using "With Open" in Python
Typeerror: 'Module' Object Is Not Callable
Beautifulsoup Grab Visible Webpage Text
Python List Sort in Descending Order
Runtimeerror on Windows Trying Python Multiprocessing