Python del vs assigning to None
Python utilizes garbage collection to free the developer from the hassle of manually handling allocating and de-allocating memory. But there are still some details that could suprise you unless you are aware of them.
Because even though the Python runtime will take care of memory management, sometimes developers will want to manually tell the garbage colletor that a variable is no longer needed. Either because they are doing some edge-case optimization and really know what they are doing or they think they are smart but have no idea what they are doing. Most likely the latter as the Python runtime most often does not release unused memory back to the operating system.
But when someone need to somehow mark a variable as ready to be freed by the garbage collector, there are usually two solutions they come across:
1x = Noneor
1del xThe difference is that x = None will free whatever it referenced but keep the name around even though it’s just referencing None (which is a type, NoneType).
On the other hand del x will completely remove both the name and what it referenced. If you thereafter try to use x an NameError will be thrown (or AttributeError in case of a object property).
So in practice, by assigning None to a name you can still use it in expressions while using del the name is completely removed. In the first case a few bytes is needed to keep the name in memory, while the later completely clears all memory usage.
1import sys
2import gc
3
4x = 'Some text here to give the variable a decent size'
5y = 2
6print('x value before deletion: {}'.format(x))
7print('x size before deletion: {} bytes'.format(sys.getsizeof(x)))
8print('y value before deletion: {}'.format(y))
9
10x = None
11del y
12gc.collect() # Not really needed, just to show garbage collection has been done hereafter
13
14print('x value after deletion: {}'.format(x))
15print('x size after deletion: {} bytes'.format(sys.getsizeof(x))) # A few bytes needed to keep symbol name
16print('x type after deletion: {}'.format(type(x)))
17
18if not x:
19 print('Can still use x!')
20
21print('y value after deletion: {}'.format(y)) # Will throw NameError (AttributeError in case of class property)Output:
1x value before deletion: Some text here to give the variable a decent size
2x size before deletion: 98 bytes
3y value before deletion: 2
4x value after deletion: None
5x size after deletion: 16 bytes
6x type after deletion: <class 'NoneType'>
7Can still use x!
8Traceback (most recent call last):
9 File "Untitled.py", line 21, in <module>
10 print('y value after deletion: {}'.format(y)) # Will throw NameError (AttributeError in case of class property)
11NameError: name 'y' is not definedAs you can see in the error above, if you for whatever reason need to hook into the memory management, unless you know the difference between assigning to None and using del it could confuse you.
Let us now look at the bytecode to see the technical difference:
1import gc
2import dis
3
4def using_none():
5 x = 1
6
7 x = None # <-- ASSIGNING TO NONE
8
9def using_del():
10 x = 1
11
12 del x # <-- USING DEL
13
14print("Bytecode when assigning to None:")
15dis.dis(using_none)
16print("\nBytecode when using del:")
17dis.dis(using_del)Output:
1Bytecode when assigning to None:
2 5 0 LOAD_CONST 1 (1)
3 2 STORE_FAST 0 (x)
4
5 7 4 LOAD_CONST 0 (None)
6 6 STORE_FAST 0 (x)
7
8 8 LOAD_CONST 0 (None)
9 10 RETURN_VALUE
10
11Bytecode when using del:
12 10 0 LOAD_CONST 1 (1)
13 2 STORE_FAST 0 (x)
14
15 12 4 DELETE_FAST 0 (x)
16 6 LOAD_CONST 0 (None)
17 8 RETURN_VALUESo using del results in only one bytecode, DELETE_FAST, while assigning to None actually runs a full assignment using LOAD_CONST and STORE_FAST. The last LOAD_CONST before RETURN_VALUE in both functions is simply the return value None that every Python function implicitly returns.