簡體   English   中英

充當 ** 解包映射的類

[英]Class that acts as mapping for **unpacking

如果沒有子類化 dict,類需要被視為映射以便它可以傳遞給帶有**的方法。

from abc import ABCMeta

class uobj:
    __metaclass__ = ABCMeta

uobj.register(dict)

def f(**k): return k

o = uobj()
f(**o)

# outputs: f() argument after ** must be a mapping, not uobj

至少到了它會拋出缺少映射功能的錯誤的地步,所以我可以開始實施。

我回顧了模擬容器類型,但簡單地定義魔術方法沒有效果,並且使用ABCMeta覆蓋並將其注冊為 dict 將斷言驗證為子類,但失敗isinstance(o, dict) 理想情況下,我什至不想使用ABCMeta

__getitem__()keys()方法就足夠了:

>>> class D:
        def keys(self):
            return ['a', 'b']
        def __getitem__(self, key):
            return key.upper()


>>> def f(**kwds):
        print kwds


>>> f(**D())
{'a': 'A', 'b': 'B'}

如果您正在嘗試創建一個 Mapping — 不僅僅是滿足傳遞給函數的要求 — 那么您真的應該從collections.abc.Mapping繼承。 文檔中所述,您只需要實現:

__getitem__
__len__
__iter__

Mixin 將為您實現其他所有內容: __contains__keysitemsvaluesget__eq____ne__

可以通過挖掘源頭找到答案。

嘗試使用帶有**的非映射對象時,會出現以下錯誤:

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()。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM