问题
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