How does CPython determine whether a user supplied an optional argument?

我是研究僧i 提交于 2020-01-07 08:08:31

问题


I started wondering how CPython can tell the difference between None as a default argument and None as a specified argument.

For example, dict.pop() will throw a KeyError if the key doesn't exist. .pop() also takes an argument for a default return value. In this example, we could set the default return to None, so: some_dict.pop(some_key, None)

If you were to define pop in Python, you'd probably have:

def pop(some_key, default=None):

In that case, it wouldn't make a difference whether you defined the default as None or just left it out, but Python can tell the difference. I did some digging in CPython and found the implementation of dict.pop():

PyObject *
_PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt)
{
    Py_hash_t hash;

    if (((PyDictObject *)dict)->ma_used == 0) {
        if (deflt) {
            Py_INCREF(deflt);
            return deflt;
        }
        _PyErr_SetKeyError(key);
        return NULL;
    }
    if (!PyUnicode_CheckExact(key) ||
        (hash = ((PyASCIIObject *) key)->hash) == -1) {
        hash = PyObject_Hash(key);
        if (hash == -1)
            return NULL;
    }
    return _PyDict_Pop_KnownHash(dict, key, hash, deflt);
}

I can understand roughly how that works. However, I can't seem to find deflt other than in that function definition (it's been suggested to me that CPython has a null pointer for None but I probably wouldn't recognise it), so I can't quite follow how CPython resolves the issue of whether or not a default was provided by the user. What is the execution path here?


回答1:


If the second argument isn't passed, deflt is a null pointer rather than pointing to None or any other Python object. This is not something you can do with a function written in Python.

The function you're looking at isn't where the default value is defined. You're looking at an intermediate helper function. The default is specified in an Argument Clinic directive next to dict_pop_impl:

/*[clinic input]
dict.pop
    key: object
    default: object = NULL
    /
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, default is returned if given, otherwise KeyError is raised
[clinic start generated code]*/

static PyObject *
dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
/*[clinic end generated code: output=3abb47b89f24c21c input=eeebec7812190348]*/
{
    return _PyDict_Pop((PyObject*)self, key, default_value);
}

See the default: object = NULL. This comment is preprocessed to generate code that looks at dict.pop's argument tuple and calls dict_pop_impl, and the generated code passes a null pointer for default_value if the argument tuple doesn't contain a value for that argument.



来源:https://stackoverflow.com/questions/59103311/how-does-cpython-determine-whether-a-user-supplied-an-optional-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!