You're correct that the id is the same inside and outside the function, but when you modify the passed in argument the id changes because you're setting the value of the parameter, not what it's referencing. In C it'd be like changing the value of a pointer in a function instead of the value referenced by a pointer in a function.
Here's an example to include the id differences and the fact that a does not take on the value of d:
>>> def f(d):
... print(f"id: '{id(d)}'")
... d = {'foo': 'bar'}
... print(f"id: '{id(d)}'")
>>> a = {}
>>> a
{}
>>> id(a)
140362610196224
>>> f(a)
id: '140362610196224'
id: '140362610196160'
>>> a
{}
>>> id(a)
140362610196224
But, as I've posted elsewhere in this thread, this example will change the contents of 'd' since d itself is not being set, its contents are:
>>> def f(d):
... d['foo'] 'bar'}
...
>>> a = {}
>>> f(a)
>>> a
{'foo': 'bar'}