Names and objects
Python keeps names and objects apart. An object holds the data and has an identity, a type and a value. A name lives in a namespace, a table that maps each name to one object. Names never hold data themselves.
The right side runs first: the list display [1, 2] builds a new list object on the heap. Then = binds the name a to it. One name refers to it, so its reference count is 1.
Sharing one object
b = a copies nothing. Python looks up the object a refers to and binds b to that same object. Two names, one object: the reference count goes up to 2.
A list is mutable: append changes the object itself, not the name. We went through b, but a refers to the very same object, so a sees [1, 2, 3] too. Names that share one object are aliases.
Every list display builds a brand-new object, even with the same contents. c refers to a second list, [1, 2, 3], with its own identity and its own reference count of 1.
== compares values: both lists hold 1, 2, 3, so True. is compares identity: are they the very same object? They are not, so False. The program prints True False.
Letting go
del a deletes the name, not the object. The list still has one reference, from b, so it stays alive and b still reads [1, 2, 3]. Its reference count drops from 2 to 1.
b = None binds b to the None object instead. Now nothing refers to the list: its count falls to 0 and CPython frees it at once, handing its memory back for reuse.
Try it yourself
;; step 1/8 >>> a = [1, 2] >>> 'a' in globals() # the module's namespace is a dict True >>> type(a) <class 'list'> >>> id(a) # identity: unique while it lives 4377851840 # yours will differ
The counts in the figure count only this program's names. CPython's real reference count also includes every other reference, such as a slot in a list or a function's argument; sys.getrefcount() shows it, usually one higher than you expect because the call itself holds a reference. Freeing an object the moment its count reaches zero is a CPython implementation detail. Other implementations, such as PyPy, may free it later. Objects that refer to each other in a cycle never reach zero on their own; CPython's cycle collector (the gc module) finds them.