Hacker News new | past | comments | ask | show | jobs | submit login

> In the mutation example you suggest, if a reference to x isn't being passed into foo, how could foo modify x

The important point is that it's not "a reference to x" that gets passed, it's a copy of x's value. x's value, like the value of all Python variables, is a reference to some object. The same thing applies to setting variables in Python in general:

  x = {1:2} # x is a new variable that references some dict 
  y = x # y is a new variable that references the same dict
  y[1] = 7 # the dict referenced by x and y was modified
  x = None # x no longer references the dict
  print(y) # y still references the dict, so this will print {1:7}
  y = None # now neither x nor y reference that dict; since y was the last reference to it, the dict's memory will be freed



This is what confuses me; it sounds like what you're saying is Python isn't pass-by-reference, it's pass-by-value, and that value is sometimes a reference?

Honestly, "x's value, like the value of all Python variables, is a reference to some object" makes me think it's more accurate to call Python pass-by-reference only.


Pass-by-reference means that your callee gets a reference to your local variables, and can modify them. This is impossible in Python. Pass by value means that your callee gets the values of your local variables and can't modify them. This is how Python functions work.

What those values represent and how they can be used is a completely different topic. Take the following code:

  x = "/dirs/sub/file.txt"
  with open(x, "w") as file:
    file.write("abc")
  foo(x)
  with open(x, "r") as file:
    print(file.read_all()) #prints "def" 

  def foo(z):
    with open(z, "w") as file:
      file.write("def")
      
Here x is in essence a "reference to a file". When you pass x to foo, it gets a copy of that reference in z. But both x and z refer to the same file, so when you modify the file, both see the changes. The calling convention is passing a copy of the value to the function. It doesn't care what that value represents.


So to be very clear:

  def foo(x):
    x['a'] = 1
  
  y = {'b': 2}
  foo(y)
  print(y)
foo can modify the object y points to, but it can't make y point to a different object? Is that what "This is impossible in Python" is referring to?


Yes.


yes! we agree how this works.

so we disagree on terminology?

in my CS upbringing, sharing the memory location of a thing as parameter was tagged "call by reference". the hallmark was: you can in theory modify the referenced thing, and you just need to copy the address.

call by value, in contrast, would create an independent clone, such that the called function has no chance to modify the outside value.

now python does fancy things, as we both agree. the result of which is that primitives (int, flot, str) behave as if they were passed by value, while dict and list and its derivatives show call by reference semantics.

I get how that _technically_ sounds like call by value. and indeed there is no assignment dunder. you can't capture reassignment of a name.

but other than that a class parameter _behaves_ like call by reference.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: