充当**解包映射的类

发布于 2024-12-22 08:49:21 字数 463 浏览 0 评论 0原文

如果没有子类化 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)isinstance(o, dict)代码>.理想情况下,我什至不想使用 ABCMeta 。

Without subclassing dict, what would a class need to be considered a mapping so that it can be passed to a method with **.

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

At least to the point where it throws errors of missing functionality of mapping, so I can begin implementing.

I reviewed emulating container types but simply defining magic methods has no effect, and using ABCMeta to override and register it as a dict validates assertions as subclass, but fails isinstance(o, dict). Ideally, I dont even want to use ABCMeta.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

要走就滚别墨迹 2024-12-29 08:49:21

__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'}

The __getitem__() and keys() methods will suffice:

>>> 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'}
冰雪之触 2024-12-29 08:49:21

如果您尝试创建映射(不仅仅是满足传递给函数的要求),那么您确实应该继承collections.abc.Mapping。如文档中所述,您需要实现只是:

__getitem__
__len__
__iter__

Mixin 将为您实现其他所有内容:__contains__keysitemsvalues获取, __eq____ne__

If you're trying to create a Mapping — not just satisfy the requirements for passing to a function — then you really should inherit from collections.abc.Mapping. As described in the documentation, you need to implement just:

__getitem__
__len__
__iter__

The Mixin will implement everything else for you: __contains__, keys, items, values, get, __eq__, and __ne__.

路还长,别太狂 2024-12-29 08:49:21

通过挖掘源头可以找到答案。

当尝试使用带有 ** 的非映射对象时,会出现以下错误:

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

The answer can be found by digging through the source.

When attempting to use a non-mapping object with **, the following error is given:

TypeError: 'Foo' object is not a mapping

If we search CPython's source for that error, we can find the code that causes that error to be raised:

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 is actually dict_merge, and the error is thrown when dict_merge returns a negative number. If we check the source for dict_merge, we can see what leads to -1 being returned:

/* 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;

The key part being:

For the latter, we only require that PyMapping_Keys() and PyObject_GetItem() be supported.

树深时见影 2024-12-29 08:49:21

使用数据类

更干净,最终证明数据类的使用质量更好,这也有助于将正确的对象返回给 keys 方法。

from dataclasses import dataclass


@dataclass(frozen=True)
class Person:
    name: str
    surname: str
    age: int
    
    def __getitem__(self, key):
        return getattr(self, key)

    def keys(self):
        return self.__annotations__.keys()


josh_doe: Person = Person("John", "Doe", 31)
print(f"John object : {josh_doe}")
user_data = {**josh_doe}
print(user_data)

Using dataclasses

Cleaner and ultimatelly turns out to be better in terms of quality, the usage of dataclass, which helps also return the correct object to keys method.

from dataclasses import dataclass


@dataclass(frozen=True)
class Person:
    name: str
    surname: str
    age: int
    
    def __getitem__(self, key):
        return getattr(self, key)

    def keys(self):
        return self.__annotations__.keys()


josh_doe: Person = Person("John", "Doe", 31)
print(f"John object : {josh_doe}")
user_data = {**josh_doe}
print(user_data)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文