a and b do not reference c, all three of them reference an object 10.
That means that executing c = 123 won't change the object behind c, it will simply point c at a new object 123, leaving a and b alone.
In other words, you have originally:
a ---+
|
b ---+---> 10-object
|
c ---+
and executing c = 123 changes that to:
a ---+
|
b ---+---> 10-object
c -------> 123-object
The sooner you grasp that Python really is fully object oriented, the faster you'll grok the language :-)
The reason why your second sample changes all the elements in l is because all the items in the l list reference the single list1 rather than distinct backing objects:
l -------> list2 {list1, list1, list1}
m -------> list1 {0-object}
Executing l[0][0] = 1 is identical to executing m[0] = 1 (see below), which will not change the binding of m to its list, or the binding of those items within list2 to list1, but rather simply change the item within list2 to be a 1-object:
l -------> list2 {list1, list1, list1}
m -------> list1 {1-object}
Hence you can see that the change to the contents of the list1 affects everything. The transcript showing that l[0][0] and m[0] are equivalent is shown below:
>>> m = [0]
>>> l = [m] * 3
>>> m
[0]
>>> l
[[0], [0], [0]]
>>> l[0][0] = 1
>>> l
[[1], [1], [1]]
>>> m
[1]
>>> m[0] = 42
>>> l
[[42], [42], [42]]
Note that if you change the binding of m, that won't change the actual bindings in list2:
>>> m = [999]
>>> l
[[42], [42], [42]]
When you do that, you end up with:
l -------> list2 {list1, list1, list1}
list1 {42-object}
m -------> list3 {999-object}