在python中,如何将类对象转换为dict

时间:2016-02-09 01:02:21

标签: python

我们说我在python中有一个简单的类

class Wharrgarbl(object):
    def __init__(self, a, b, c, sum, version='old'):
        self.a = a
        self.b = b
        self.c = c
        self.sum = 6
        self.version = version

    def __int__(self):
        return self.sum + 9000

    def __what_goes_here__(self):
        return {'a': self.a, 'b': self.b, 'c': self.c}

我可以很容易地将它转换为整数

>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> int(w)
9006

哪个好极了!但是,现在我想以类似的方式将它投入一个字典

>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}

我需要为此定义什么?我尝试将__dict__dict替换为__what_goes_here__,但dict(w)在两种情况下都产生了TypeError: Wharrgarbl object is not iterable。我不认为简单地使类可迭代将解决问题。我还尝试了许多谷歌,其中包括" python cast对象的多种不同措辞,以及#34;我能想到但却无法找到相关内容:{

另外!请注意,调用w.__dict__不会做我想做的事情,因为它会包含w.versionw.sum。我希望自定义转换为dict,方法与使用int自定义转换为def int(self)的方式相同。

我知道我可以做这样的事情

>>> w.__what_goes_here__()
{'a': 'one', 'c': 'three', 'b': 'two'}

但我假设有一种pythonic方法可以使dict(w)起作用,因为它与int(w)str(w)属于同一类型。如果没有更多的pythonic方式,这也很好,只是想我会问。哦!我想,因为它很重要,这是针对python 2.7,但也是一个2.4旧的破坏点的超级奖励积分。

还有另一个问题Overloading __dict__() on python class与此类似,但可能有所不同,以保证这不是重复。我相信OP正在询问如何将类对象中的所有数据转换为字典。我正在寻找一种更加个性化的方法,因为我不希望__dict__中包含的dict()中的所有内容都包含在asdict中。像公共变量和私有变量之类的东西可能足以解释我正在寻找的东西。这些对象将存储计算中使用的一些值,这样我就不需要/想要显示在结果词典中。

更新: 我已选择使用建议的get("/*", BaseWebApp::notFound, new JsonTransformer()); put("/*", BaseWebApp::notFound, new JsonTransformer()); post("/*", BaseWebApp::notFound, new JsonTransformer()); delete("/*", BaseWebApp::notFound, new JsonTransformer()); 路线,但选择我想要的答案是一个艰难的选择。 @RickTeachey和@ jpmc26都提供了我即将推出的答案,但前者有更多的信息和选项,同样也获得了相同的结果,并且被更多投票,所以我选择了它。尽管如此,还是全力以赴,感谢您的帮助。我已经潜伏了很长时间并且很难进行堆叠溢出,而且我试图让我的脚趾在水中更多。

8 个答案:

答案 0 :(得分:54)

至少有五种方法。首选方式取决于您的用例。

选项1:只需添加asdict()方法。

根据问题描述,我会非常考虑其他答案所建议的asdict做事方式。这是因为您的对象看起来并不是一个集合:

class Wharrgarbl(object):

    ...

    def asdict(self):
        return {'a': self.a, 'b': self.b, 'c': self.c}

使用下面的其他选项可能会让其他选项感到困惑,除非确切地说明哪些对象成员将被迭代或不被迭代或指定为键值对。

选项1a:使用_asdict method提供的'typing.NamedTuple'(或大致相同的'collections.namedtuple')。

使用命名元组是一种非常方便的方法,可以轻松地为您的班级添加大量功能,包括an _asdict method。但是,一个限制是NT将在其_asdict中包含所有成员。因此,如果您有不想加入的成员,则需要修改_asdict结果:

from typing import NamedTuple

class Wharrgarbl(NamedTuple):
    a: str
    b: str
    c: str
    sum: int = 6
    version: str = 'old'

    def _asdict(self):
        d = super()._asdict()
        del d['sum']
        del d['version']
        return d

另一个限制是NT是只读的。这可能是也可能不是。

选项2:实施__iter__

像这样,例如:

def __iter__(self):
    yield 'a', self.a
    yield 'b', self.b
    yield 'c', self.c

现在你可以这样做:

dict(my_object)

这是有效的,因为dict()构造函数接受(key, value)对的迭代来构造字典。在此之前,问问自己这样一个问题:是否以这种方式将对象迭代为一系列键值对,同时方便创建dict - 在其他上下文中实际上可能是令人惊讶的行为。例如,问自己问题" list(my_object)的行为应该是什么......?"

此外,请注意,使用get item obj["a"]语法直接访问值将不起作用,并且关键字参数解包不会起作用。对于那些,您需要实现映射协议。

选项3: Implement mapping protocol。这允许按键访问行为,在不使用dict的情况下转换为__iter__,还提供关键字解包行为。

映射协议要求您(至少)提供两种方法:keys()__getitem__

class MyKwargUnpackable:
    def keys(self):
        return list("abc")
    def __getitem__(self, key):
        return dict(zip("abc", "one two three".split()))[key]

现在你可以做以下事情:

>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m)  # cast to dict directly
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m)  # unpack as kwargs
{'a': 'one', 'b': 'two', 'c': 'three'}

请注意,直接将对象转换为__iter__时,映射协议优先于 dict方法(不使用kwarg解包,即dict(m)) 。因此,当用作可迭代(例如list(m))与转换为dictdict(m))时,导致对象具有不同的行为是可能的 - 有时是方便的。

EMPHASIZED :仅仅因为您可以使用映射协议,并不意味着您应该这样做 。实际上有意义您的对象是作为一组关键字参数传递的吗?通过键访问它 - 就像字典一样 - 真的有意义吗?

如果这些问题的答案是,那么考虑下一个选项可能是个好主意。

选项4:请使用'collections.abc' module

将您的班级从'collections.abc.Mapping'collections.abc.MutableMapping信号传递给其他用户,出于所有意图和目的,您的班级是一个映射 *并且可以预期其行为方式。

您仍然可以根据需要将对象转换为dict,但可能没有理由这样做。由于duck typing,在大多数情况下,将映射对象强制转换为dict只会是一个额外的不必要的步骤。

This answer也可能会有所帮助。

如下面的评论中所述:值得一提的是,使用abc方式实际上将您的对象类转换为类似dict的类(假设您使用MutableMapping而不是只读Mapping基类)。你可以使用dict做的所有事情,你可以使用自己的类对象。这可能是,也可能不是。

还要考虑查看numbers模块中的数字abcs:

https://docs.python.org/3/library/numbers.html

由于您还要将对象投射到int,因此将您的课程转变为完全成熟的int可能更有意义,因此无需投射。

选项5:查看使用dataclasses module(仅限Python 3.7),其中包含方便的asdict()实用程序方法。

from dataclasses import dataclass, asdict, field, InitVar

@dataclass
class Wharrgarbl(object):
    a: int
    b: int
    c: int
    sum: InitVar[int]  # note: InitVar will exclude this from the dict
    version: InitVar[str] = "old"

    def __post_init__(self, sum, version):
        self.sum = 6  # this looks like an OP mistake?
        self.version = str(version)

现在你可以这样做:

    >>> asdict(Wharrgarbl(1,2,3,4,"X"))
    {'a': 1, 'b': 2, 'c': 3}

*"制图"已成为标准"名称" dict - 像鸭子类型

答案 1 :(得分:17)

没有神奇的方法可以做你想要的。答案就是恰当地命名。对于asdict的简单转换,dict是一个合理的选择,主要受namedtuple启发。但是,您的方法显然会包含特殊逻辑,该逻辑可能不会立即从该名称中显而易见;你只返回类'state的一个子集。如果你能提出一个稍微冗长的名字,清楚地传达概念,那就更好了。

其他答案建议使用__iter__,但除非您的对象是真正可迭代的(代表一系列元素),否则这实际上没什么意义,并且构成了对该方法的笨拙滥用。事实上,你想要过滤掉某些类的状态,这使得这种方法更加可疑。

答案 2 :(得分:9)

这样的事情可能会起作用

class MyClass:
    def __init__(self,x,y,z):
       self.x = x
       self.y = y
       self.z = z
    def __iter__(self): #overridding this to return tuples of (key,value)
       return iter([('x',self.x),('y',self.y),('z',self.z)])

dict(MyClass(5,6,7)) # because dict knows how to deal with tuples of (key,value)

答案 3 :(得分:9)

我认为这对你有用。

class A(object):
    def __init__(self, a, b, c, sum, version='old'):
        self.a = a
        self.b = b
        self.c = c
        self.sum = 6
        self.version = version

    def __int__(self):
        return self.sum + 9000

    def __iter__(self):
        return self.__dict__.iteritems()

a = A(1,2,3,4,5)
print dict(a)

输出

{'a':1,'c':3,'b':2,'sum':6,'version':5}

答案 4 :(得分:3)

与许多其他人一样,我建议实现一个to_dict()函数而不是(或者除此之外)允许转换为字典。我认为这更加明显,该类支持这种功能。您可以轻松实现这样的方法:

def to_dict(self):
    class_vars = vars(MyClass)  # get any "default" attrs defined at the class level
    inst_vars = vars(self)  # get any attrs defined on the instance (self)
    all_vars = dict(class_vars)
    all_vars.update(inst_vars)
    # filter out private attributes
    public_vars = {k: v for k, v in all_vars.items() if not k.startswith('_')}
    return public_vars

答案 5 :(得分:2)

如果不了解问题的整个背景,很难说,但我不会覆盖__iter__

我会在课堂上实现__what_goes_here__

as_dict(self:
    d = {...whatever you need...}
    return d

答案 6 :(得分:1)

这是非常干净快速的解决方案 我创建了一个函数,可以将任何自定义类转换为 dict

def convert_to_dict(args: dict):
json = dict()
for key, value in args.items():
    key_vals = str(key).split("_")
    last_index = len(key_vals)
    json[str(key_vals[last_index-1])] = value
return json

你需要的是提供它 object.__dict__ 然后它会帮你清理它并帮助你将它存储在数据库中。

答案 7 :(得分:0)

我正在尝试编写一个listdict都属于的类。我希望程序员能够同时将此对象“投射”到list(放下键)或dict(带有键)。

看看Python当前执行dict()强制转换的方式:它使用传递的对象调用Mapping.update()。这是the code from the Python repo

def update(self, other=(), /, **kwds):
    ''' D.update([E, ]**F) -> None.  Update D from mapping/iterable E and F.
        If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
        If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
        In either case, this is followed by: for k, v in F.items(): D[k] = v
    '''
    if isinstance(other, Mapping):
        for key in other:
            self[key] = other[key]
    elif hasattr(other, "keys"):
        for key in other.keys():
            self[key] = other[key]
    else:
        for key, value in other:
            self[key] = value
    for key, value in kwds.items():
        self[key] = value

if语句的最后一个子案例(在other上进行迭代)是大多数人想到的。但是,如您所见,也可以具有keys()属性。结合__getitem__()应该可以轻松地将子类正确地转换为字典:

class Wharrgarbl(object):
    def __init__(self, a, b, c, sum, version='old'):
        self.a = a
        self.b = b
        self.c = c
        self.sum = 6
        self.version = version

    def __int__(self):
        return self.sum + 9000

    def __keys__(self):
        return ["a", "b", "c"]

    def __getitem__(self, key):
        # have obj["a"] -> obj.a
        return self.__getattribute__(key)

这将起作用:

>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}