Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PYTHON: double-underscore prefixed parameter in *function*

Tags:

python

cpython

belows is in builtins.pyi


def max(__arg1: _T, __arg2: _T, *_args: _T, key: Callable[[_T], SupportsLessThan]) -> _T:

I do know what the name mangling means and know that the name mangling will influence every "__xxx" identifier as long as in class definition field.

So I have three questions:

  • why there paramters "__arg1" "__arg2" used the "__xxx" pattern and parameter "key" not;
  • what is the function and purpose of "__xxx" pattern specially used in the function's parameter or in this case;
  • I feel awkward to find the implementation of name mangling, and could you tell me where it is in cpython source code.

Many thanks.

further work:

def wat1(__a):
    def wat2(__b):
        return __b, __a
    return __a

print(wat1.__code__.co_varnames)
print(wat1.__code__.co_cellvars)
dis.dis(wat1)

output(linux python==3.10.8):

('__a', 'wat2')
('__a',)

21           0 LOAD_FAST                0 (__a)
              2 RETURN_VALUE

22           0 LOAD_FAST                0 (__b)
              2 LOAD_DEREF               0 (__a)
              4 BUILD_TUPLE              2
              6 RETURN_VALUE


like image 992
hoshino Avatar asked Oct 19 '25 12:10

hoshino


2 Answers

Name mangling applies only to names used in a class definition, not function parameters. In this case the leading underscores are only a naming convention to indicate that parameters with such names are not to be passed with keyword arguments, but rather only positional ones.

This is to say that you should call max with:

max(1, 2)

rather than:

max(__arg1=1, __arg2=2)

The key argument on the other hand, are named without an underscore prefix, indicating that it is meant to be passed with a keyword argument:

max(1, -2, key=abs)
like image 79
blhsing Avatar answered Oct 22 '25 05:10

blhsing


Thanks for @blhsing's answer and @zvone's comment, to summarize the above, I' d like to share my understand of it.
Here is cpython source code part of max() :

static PyObject *
min_max(PyObject *args, PyObject *kwds, int op)
{
    PyObject *v;
    static char *kwlist[] = {"key", "default", NULL};
    const int positional = PyTuple_Size(args) > 1;
    int ret;

    if (positional) {
        v = args;
    }
    else if (!PyArg_UnpackTuple(args, name, 1, 1, &v)) {
        if (PyExceptionClass_Check(PyExc_TypeError)) {
            PyErr_Format(PyExc_TypeError, "%s expected at least 1 argument, got 0", name);
        }
        return NULL;
    }

If using keyword argument to pass args to max(), arguments here will drop in PyObject* kwds rather than PyObject* args so PyArg_UnpackTuple will get nothing and return false, then raise a TypeError in python level as expected.

So it's a powerful purpose to use positional one here?

Also it's just a convention now with no constraint but new positional only arguments syntax do restrict. So belows is possible.

def wat1(__a):
    return __a
wat1(__a=1)

New one would be preferred instead of the old naming convention because it make input arguments more contrllable.

For some reason builtin.pyi don't apply this new positional syntax, maybe historical issues? I cannot figure it out.

like image 24
hoshino Avatar answered Oct 22 '25 06:10

hoshino