theMobDog theMobDog - 1 month ago 7
Python Question

python (deI) statement and python behavior

When a del statement is issued:


del var


Shouldn't it be removed from the list of known variable and shouldn't the python interpreter spit out "unresolved reference" error?

Or is it simply just deleting the object and leaving the name (var) not pointing anywhere? Why would that kind of behavior be useful? In what cases?

Also I am talking simply about deleting a single variable. Not del list[3] or alike.

note: I am asking if this python's behavior is meant that way. And in what cases, it would be still useful.

Answer

Not sure what you're asking, as clearly thats what happens...

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = 10
>>> 'x' in vars()
True
>>> vars()['x']
10
>>> del x
>>> 'x' in vars()
False
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

As you can see, Python stores valid identifiers in vars() and locals()... So del essentially removes it from memory, removing it from these data structures as well. So the identifier is no longer valid. It's not like in C where you might free some memory and set the value to NULL.

>>> x = 10
>>> def func():
...   global x
...   del x
...
>>> x
10
>>> func()
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>>

Update:

Raymond made a good point when he mentioned that the object itself (read: the actual data in memory that the identifier points too, for lack of a more detailed explanation) is only freed when it's reference count goes to 0. IMO he could have done a better job detailing this in the python interpreter, so I'll have a go at it.

We'll use the ID function to prove this:

Help on built-in function id in module __builtin__:

id(...)
    id(object) -> integer

    Return the identity of an object.  This is guaranteed to be unique among
    simultaneously existing objects.  (Hint: it's the object's memory address.)

Pay attention to whats going on here, you'll see that for immutable types, different values reference the same memory (strings are immutable in python and are interned, which means only one copy of each unique string is stored -- in ruby, symbols are interned strings).

>>> import sys
>>> x = 10     # 10 is a common value, probably exists in memory already
>>> sys.getrefcount(x)
26
>>> id(x)      # memory location of x
140266396760096
>>> y = x
>>> id(y) == id(x)
True
>>> z = 10
>>> id(z) == id(y) == id(x)
True
>>> sys.getrefcount(y)
28
>>> sys.getrefcount(z)
28
>>> del y, z
>>> sys.getrefcount(x)
26
>>> del x
>>> x = 'charlie'
>>> id(x)
4442795056
>>> y = 'charlie'
>>> z = x
>>> id(x) == id(y) == id(z)
True
>>> sys.getrefcount(x)
4
>>> sys.getrefcount(y)
4
>>> sys.getrefcount(z)
4
>>> del y
>>> del x
>>> sys.getrefcount(z)     # will be two because this line is an additional reference
2
>>> id(z)                  # pay attention to this memory location because this 
4442795056                 # is the last remaining reference to 'charlie', and
>>> del z                  # when it goes out of scope 'charlie' is removed from
>>>                        # memory.
>>> id('charlie')          # This has a different memory location because 'charlie'
4442795104                 # had to be re-created.

First we set the identifier 'x' == 10, a common integer value. Since 10 is such a common value, it's almost guaranteed that something in the processes memory already has that value. Since integers are immutable in python, we only ever need one copy of each unique value stored in memory. In this case, there are 24 other references to 10 in memory. Setting x = 10 creates the 25th reference, and calling sys.getrefcount(x) is the 26th reference (though it quickly goes out of scope). When we set y = 10 and z = x, we know that they all point to the same data because they all have the same memory location. Calling del alters the reference count, but even when all 3 are deleted the integer 10 still exists in memory.

Next we create set x = 'charlie', followed by y = 'charlie', and finally z = x. You can see all of these variables have the same memory address. Once we delete all of these variables there are no more references to 'charlie'. We can verify this by calling id('charlie') which will produce a different memory address meaning the string did not exist in memory when we called that function.

One more thing to note is the location of 'charlie' and 10 in memory. 10 has a significantly higher memory address than Charlie does. This is because they exist in different locations of memory. 'charlie' exists on the heap whereas 10 exists on the stack.

>>> hex(id(10))         # high address, this is on the stack
'0x7f9250c0b820'
>>> hex(id('charlie'))  # lower address, this is on the heap
'0x108cfac60