I don't think that's true, as the function will never be able to change the binding, and only gets a reference to the value of the binding. If the function was passed a particular namespace binding, it would be able to change this binding's value, and that's not possible.
I agree that "pass by reference" is a misleading way to describe Python functions.
More precisely, the function's arguments when it is called are a set of values for variables taken from the caller's namespace, which then get bound to the corresponding names in the local namespace. You're right that "passing the binding" doesn't really describe that process very well.
Again, this is very obviously false, since a call need not involve any variables at all:
foo(1, 2, "three")
The function's arguments when it is called are a set of values taken from the caller, yes. And those values are obtained from evaluating expressions that may involve variables. But they might not. Saying that the values are "a set of values for variables taken from the caller's namespace" is wrong.You are mistaken, and your talk of namespace bindings is just obfuscation. Python arguments are passed by pointer. Python namespaces bind names to pointers. There is no "binding" object passed in a function call.
True, in which case the values would just be constants. But they will still get bound to names in the function's local namespace.
> You are mistaken
No, I left out a case which, it seemed to me, did not affect the main point I was making. If you want to make clear that that's a possible case, fine, you've done so. But you haven't refuted (or even engaged with) my main point at all.
> your talk of namespace bindings is just obfuscation
No, it's a very important difference between what Python variables mean and what variables in language like C mean. You might think the difference is unimportant, but not everyone agrees with you.
> Python arguments are passed by pointer. Python namespaces bind names to pointers.
At the C level inside the interpreter, yes, this is true: every "object", such as the 1, 2, and "three" in your example, is a pointer to a C struct containing the object's data (sometimes including further pointers). Again, that doesn't affect my main point at all.
> There is no "binding" object passed in a function call.
I already agreed to this in my response to pstch upthread (the post of mine you originally replied to). Once more, it doesn't affect my main point at all.
OK. Your main point (now that you have shifted the goalposts) seems to be that argument passing induces a binding of the parameter name to the argument value, yes?
In the abstract, this point is meaningless since it applies equally to every other programming language. In this C function:
void foo(int x, float y, char *z) { ... }
the C compiler also has to manage bindings from variable names to the locations where their values are stored. It needs that information to find the correct values in registers or (just like in Python) on the stack.In the concrete, the point is also meaningless since those bindings do not involve dictionaries as you seem to think. For positional functional arguments, loooong before the call takes place, the bytecode compiler resolves names to stack indices, and the variables are accessed through those. Just like C can access arguments passed on the stack using constant offsets from the stack pointer.
Here is the code for the normal call fast path: https://github.com/python/cpython/blob/62be74290aca26d16f3f5...
f = _PyFrame_New_NoTrack(tstate, co, globals, NULL);
if (f == NULL) {
return NULL;
}
fastlocals = f->f_localsplus;
for (i = 0; i < nargs; i++) {
Py_INCREF(*args);
fastlocals[i] = *args++;
}
result = PyEval_EvalFrameEx(f,0);
This sets up a stack frame for the callee (on the heap, yes), then copies the arguments (which are pointers to PyObject) into slots in that stack frame numbered consecutively from 0.Access to these arguments inside the callee is via the LOAD_FAST bytecode instruction: https://github.com/python/cpython/blob/master/Python/ceval.c...
case TARGET(LOAD_FAST): {
PyObject *value = GETLOCAL(oparg);
...
which uses the GETLOCAL macro: https://github.com/python/cpython/blob/master/Python/ceval.c... #define GETLOCAL(i) (fastlocals[i])
Nowhere are name-value bindings allocated dynamically in this normal case. Python does perform dictionary manipulation for keyword args, but not for positional ones. For normal positional arguments, the mechanism is exactly equivalent to a C (or whatever) compiler pushing arguments onto the stack. Local variables are exactly what you claimed that they were not, namely names for storage locations (stack slots).I'm done with this thread now.
It's that there is an extra step involved that does not occur in languages like C.
> those bindings do not involve dictionaries as you seem to think
For positional arguments, you are correct. But, as you note, there is still an extra memory allocation on the heap, because the stack at the Python level uses heap memory, not stack memory, at the C level.
For keyword arguments, as you agree, there is a dictionary entry created.
> Local variables are exactly what you claimed they were not
More precisely, local variables inside a function that correspond to positional arguments are, for performance reasons, stored in an array of function local pointers at the C level, instead of being stored as dictionary entries (namespace bindings).
But the fact that this is a particular exception to Python's normal treatment of variables simply highlights the point I was making.
Can you explain the difference? What is a "namespace binding"? When accessing a variable's value, the interpreter's code sure looks like it treats the variable name as the name of a storage location.
> Passing a variable to a function means passing a particular namespace binding
"Passing a variable to a function" is not a thing. Passing a value to a function means passing a pointer to that value. This is the same for "foo(some_var)" and "foo(1)". Since "1" is presumably not a "namespace binding", you seem to suggest that there are different parameter passing mechanisms for these cases. There aren't.
We could call this "passing references to objects by value."
But in any case, that's the point that made me go wtf while reading through as well. Whatever you want to call it, it's the same as what C (when not passing pointers), Java, PHP, Javascript, and many many other languages use.
Which is why it's so frustrating we don't have a good, easily-understood/well-known name for it.
I have no idea why would you mention BASIC here.
An entry in a Python dictionary that is the namespace for the current scope.
For example, to repeat a response I gave to someone else upthread, compare a variable assignment in C and Python.
In C:
int i = 17;
Means "set aside one int's worth of storage and fill it with the bit pattern for the number 17".In Python:
i = 17
means "create an int object with the value 17 and bind it in the current namespace dictionary to the name i".Set aside one pointer's worth of storage on the heap, yes; not on the stack or in the program's data segment, which is what the C statement I gave does.
> and fill it with the bit pattern for the pointer pointing to the value 17 (which you may have to create first)"
Which has no analogue whatever in the C version.
Also, you completely left out the part about creating an entry in the namespace dictionary, which also has no analogue whatever in the C version, and which involves additional memory allocations.
Except for locals. (Well, OK, the Python stack is reified in the heap, but local variables are translated into small integer indices into the stack frame.)
> an entry in the namespace dictionary
Except for locals, which do not involve memory allocations or dictionary lookups.
Anyway, we're getting farther and farther away from the topic, which was that Python argument passing is no different from any other language in which everything is an object.
I meant the C stack, not the Python stack. At the C level all Python objects are allocated on the heap (except for some of the singletons like None which are statically allocated by the interpreter). (The fact that I used the term "heap" indicates that I was talking about the C level, since there is no "heap" at the Python level.)
> Python argument passing is no different from any other language in which everything is an object.
I disagree, since again you have left out the namespace binding step completely.