[英]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__
、 keys
、 items
、 values
、 get
、 __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.