CPython 如何确定用户是否提供了可选参数?

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

我开始想知道 CPython 如何区分作为默认参数的 None 和作为指定参数的 None 之间的区别。

例如,如果密钥不存在,dict.pop() 将抛出 KeyError.pop() 也接受默认 return 值的参数。在此示例中,我们可以将默认值 return 设置为 None,因此:some_dict.pop(some_key, None)

如果您要在 Python 中定义 pop,您可能会:

def pop(some_key, default=None):

在那种情况下,将 default 定义为 None 还是将其省略都没有什么区别,但是 Python 可以分辨出区别。我在 CPython 中做了一些挖掘,发现了 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);
}

我能大致理解它是如何工作的。但是,除了那个函数定义之外,我似乎找不到 deflt (有人向我建议 CPython 有一个 None 的空指针,但我可能不认识它), 所以我不太明白 CPython 是如何解决用户是否提供默认值的问题的。这里的执行路径是什么?

如果没有传递第二个参数,deflt 是一个空指针,而不是指向 None 或任何其他 Python 对象。这不是用 Python.

编写的函数可以做的事情

您查看的函数未定义默认值。您正在查看中间辅助函数。默认值在 dict_pop_impl:

旁边的 Argument Clinic 指令中指定
/*[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);
}

default: object = NULL。此注释经过预处理以生成查看 dict.pop 的参数元组并调用 dict_pop_impl 的代码,如果参数元组不包含该参数的值。