In a case like this, you'd have to copy the dictionary if you want to change it and keep the changes local to the function.
The reason is that, when you pass a dictionary into your second bar function, Python only passes a reference to the dictionary. So when you modify it inside the function, you're modifying the same object that exists outside the function. In the first bar function, on the other hand, you assign a different object to the name foo inside the function when you write foo = "bar". When you do that, the name foo inside the function starts to refer to the string "bar" and not to the string "foo". You've changed which object the name refers to. But foo inside the function and foo outside the function are different names, so if you change the object labeled by foo inside the function, you don't affect the name foo outside the function. So they refer to different objects.