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

问题描述 投票:54回答:6

假设我在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 object to dict”,正如我想象的那样,却找不到任何相关内容:{

也!注意如何调用w.__dict__不会做我想要的,因为它将包含w.versionw.sum。我想用dict自定义转换为int,就像我可以使用def int(self)自定义转换为>>> w.__what_goes_here__() {'a': 'one', 'c': 'three', 'b': 'two'} 一样。

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

dict(w)

但我假设有一种pythonic方式使int(w)工作,因为它是与str(w)Overloading __dict__() on python class相同类型的东西。如果没有更多的pythonic方式,那也没关系,我想问一下。哦!我想因为它很重要,这是针对python 2.7,但是对于2.4旧的破坏解决方案也是超级奖励积分。

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

更新:我选择了建议的asdict()路线,但选择我想要的答案是一个艰难的选择。 @RickTeachey和@jpmc26都提供了我将要推出的答案,但前者有更多的信息和选项,并且也获得了相同的结果,并且被更多投票,所以我选择了它。尽管如此,还是全力以赴,感谢您的帮助。我已经潜伏在stackoverflow上,并且我试图让我的脚趾更多地在水中。

python
6个回答
57
投票

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

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

根据问题描述,我会非常考虑class Wharrgarbl(object): ... def asdict(self): return {'a': self.a, 'b': self.b, 'c': self.c} 的做事方式由其他答案建议。这是因为您的对象看起来并不是一个集合:

_asdict method

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

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

使用命名元组是一种非常方便的方法,可以通过最少的工作量为您的类添加大量功能,包括_asdict。但是,一个限制是NT将包括其_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 结果:

__iter__

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

选项2:实施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)-可能实际上是在其他环境中令人惊讶的行为。例如,问自己一个问题“obj["a"]的行为应该是什么......?”

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

选项3: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时,映射协议优先于dict(m)方法(不使用kwarg解包,即list(m))。因此,当用作可迭代(例如,dict)与投射到dict(m)'collections.abc' module)时,使对象具有不同的行为是可能的 - 并且有时是方便的。

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

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

选项4:考虑使用'collections.abc.Mapping

'collections.abc.MutableMappingdict继承你的类向其他用户发出信号,为了所有意图和目的,你的类是一个映射*,并且可以期望这样做。

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

dict也许会有所帮助。

正如下面的评论中所指出的:值得一提的是,以abc方式执行此操作实质上将您的对象类转换为类似MutableMapping的类(假设您使用Mapping而不是只读dict基类)。你可以用numbers做的一切,你可以用你自己的类对象。这可能是,也可能不是。

还要考虑查看https://docs.python.org/3/library/numbers.html模块中的数字abcs:

int

既然你也把你的对象转换为int,那么将你的类变成一个完整的dataclasses module可能更有意义,这样就不必进行投射了。

选项5:使用asdict()(仅限Python 3.7),包括一个方便的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

*“制图”已成为asdict式鸭子的标准“名称”


17
投票

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

其他答案建议使用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) ,但除非您的对象是真正可迭代的(代表一系列元素),否则这实际上没有多大意义,并构成了对该方法的尴尬滥用。事实上,你想要过滤掉一些类的状态,这使得这种方法更加可疑。


11
投票

我认为这对你有用。

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)

产量

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


10
投票

像这样的东西可能会奏效

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

3
投票

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

__iter__

2
投票

如果不知道问题的整个背景,很难说,但我不会覆盖__what_goes_here__

我会在课堂上实施as_dict(self: d = {...whatever you need...} return d

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.