当我写**object_identifier时,有没有一个被调用的神奇方法?
当我写**object_identifier时,有没有一个被调用的神奇方法?
如果不使用字典的子类化,要将一个类视为映射并将其传递给带有**
的方法,需要满足什么条件?\n
from abc import ABCMeta class uobj: __metaclass__ = ABCMeta uobj.register(dict) def f(**k): return k o = uobj() f(**o) # 输出:f()参数后的**必须是映射,而不是uobj
\n至少要能抛出映射功能缺失的错误,这样我才能开始实现。\n我已经查看了模拟容器类型的方法,但仅定义魔术方法没有效果,并且使用ABCMeta
来覆盖并将其注册为字典时,验证断言作为子类是有效的,但是isinstance(o, dict)
则失败。理想情况下,我甚至不想使用ABCMeta
。
有没有一个魔法方法在我写**object_identifier时被调用?
这个问题的出现原因是,当我们试图使用一个非映射对象与**一起使用时,会出现以下错误:
TypeError: 'Foo' object is not a mapping
通过查看CPython的源代码,我们可以找到引发此错误的代码:
case TARGET(DICT_UPDATE): {
PyObject *update = POP();
PyObject *dict = PEEK(oparg);
if (PyDict_Update(dict, update) < 0) {
if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object is not a mapping",
Py_TYPE(update)->tp_name);
PyDict_Update实际上是dict_merge,并且在dict_merge返回负数时抛出错误。如果我们查看dict_merge的源代码,我们可以看到导致返回-1的原因:
/* We accept for the argument either a concrete dictionary object,
* or an abstract "mapping" object. For the former, we can do
* things quite efficiently. For the latter, we only require that
* PyMapping_Keys() and PyObject_GetItem() be supported.
*/
if (a == NULL || !PyDict_Check(a) || b == NULL) {
PyErr_BadInternalCall();
return -1;
关键部分是:
对于后者,我们只需要支持PyMapping_Keys()和PyObject_GetItem()。
这是否意味着只有getitem和keys需要为ti定义?
是的,这正是这个注释所暗示的。这个发现也在最佳答案中得到了反映。我发表了这个答案,以展示如何找到这样的东西。
问题的原因是,当我们写下一个对象的标识符时,是否有一个魔法方法会被调用?解决方法是,如果我们想创建一个映射(不仅仅是满足传递给函数的要求),我们应该继承自`collections.abc.Mapping`。根据文档的描述,我们只需要实现以下方法:`__getitem__`、`__len__`和`__iter__`。Mixin将为我们实现其他所有方法:`__contains__`、`keys`、`items`、`values`、`get`、`__eq__`和`__ne__`。该方法已经移至`collections.abc`包中。
如果我们想创建一个映射对象,并且希望在写入对象标识符时自动调用某个方法,我们应该继承自`collections.abc.Mapping`并实现`__getitem__`、`__len__`和`__iter__`方法。这样,我们就可以利用Mixin自动实现其他相关方法。
在上面的代码中,通过定义类D并实现了keys()和__getitem__()方法来解决了问题。当调用f()函数时,传入的参数是D()对象,这里使用了**kwds的形式进行参数传递,**表示将D()对象的keys()方法返回的键作为参数传递给f()函数。在f()函数中,通过print语句输出了传入的参数,即{'a': 'A', 'b': 'B'}。
在这个例子中,我们可以看到当使用**object_identifier的形式进行参数传递时,会调用对象的keys()方法来获取键,并且调用对象的__getitem__()方法来获取对应键的值。这是因为在Python中,**object_identifier会将对象的keys()方法返回的键作为参数传递给函数,并且将调用对象的__getitem__()方法来获取对应键的值。
因此,如果想要在使用**object_identifier时自定义键和值的获取方式,可以通过在对象中实现keys()和__getitem__()方法来实现。
以上就是针对问题的解决方法和出现的原因的整理。通过定义类并实现特定的方法,我们可以在使用**object_identifier时自定义键和值的获取方式。