I find that with Python that's almost always caused by not quite understanding the underlying rules. Once understood, they're very consistent.
For example: does Python pass function arguments by value or by reference? Neither! It passes them by object reference - not by variable reference like C/C++.
Check out:
>>> def foo(a):
... a = 2
...
>>> value = 1
>>> value
1
>>> foo(value)
>>> value
1
and:
>>> def mutate(dct):
... dct['foo'] = 'bar'
...
>>> value = {}
>>> value
{}
>>> mutate(value)
>>> value
{'foo': 'bar'}
This apparent contradiction confuses a lot of people. The first example would imply that Python's pass-by-value, but the second looks a lot like pass-by-reference. If you don't know the actual answer, it looks magical and inconsistent, and I've heard all sorts of explanations like "mutable arguments are passed by reference while immutable objects are passed by value".
In reality, the object itself - not the variable name referring to the object - is passed to a function arguments. In the first example we're passing in the object `int(1)`, not the variable `value`, and creating a new variable `a` to refer to it. When we then run `a = 2`, we're creating a new object `int(2)` and altering `a` to point to the new object instead of the old one. Nothing happens to the old `int(1)` object. It's still there, and the top-level `value` variable still points to it. `a` is just a symlink: it doesn't have a value of its own. Neither does `value` or any other Python variable name. That's why the second example works: we're passing in the actual dictionary object and then mutating it. We're not passing in the variable `value`; we're passing in the object that `value` refers to.
The point of this long-windedness is that Python's rules tend to be very, very simple and consistent. Its behavior can be unexpected if you don't truly understand the details or if you try to infer parallels to other languages by observing it and hoping you're right.
Except that's 100% wrong here. There's not even a facility for marking an object as immutable in Python, so that wouldn't support user-defined classes at all.
Python is always pass-by-object-reference, and assignment is always a pointer operation. It's not special-cased like you're describing.
For example: does Python pass function arguments by value or by reference? Neither! It passes them by object reference - not by variable reference like C/C++.
Check out:
and: This apparent contradiction confuses a lot of people. The first example would imply that Python's pass-by-value, but the second looks a lot like pass-by-reference. If you don't know the actual answer, it looks magical and inconsistent, and I've heard all sorts of explanations like "mutable arguments are passed by reference while immutable objects are passed by value".In reality, the object itself - not the variable name referring to the object - is passed to a function arguments. In the first example we're passing in the object `int(1)`, not the variable `value`, and creating a new variable `a` to refer to it. When we then run `a = 2`, we're creating a new object `int(2)` and altering `a` to point to the new object instead of the old one. Nothing happens to the old `int(1)` object. It's still there, and the top-level `value` variable still points to it. `a` is just a symlink: it doesn't have a value of its own. Neither does `value` or any other Python variable name. That's why the second example works: we're passing in the actual dictionary object and then mutating it. We're not passing in the variable `value`; we're passing in the object that `value` refers to.
The point of this long-windedness is that Python's rules tend to be very, very simple and consistent. Its behavior can be unexpected if you don't truly understand the details or if you try to infer parallels to other languages by observing it and hoping you're right.