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

pen*_*ant 65 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}
Run Code Online (Sandbox Code Playgroud)

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

>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> int(w)
9006
Run Code Online (Sandbox Code Playgroud)

哪个好极了!但是,现在我想以类似的方式把它变成一个字典

>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}
Run Code Online (Sandbox Code Playgroud)

我需要为此定义什么?我试图取代这两个__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'}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ric*_*ica 73

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

选项1:只需添加一种asdict()方法.

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

class Wharrgarbl(object):

    ...

    def asdict(self):
        return {'a': self.a, 'b': self.b, 'c': self.c}
Run Code Online (Sandbox Code Playgroud)

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

选项2:覆盖'typing.NamedTuple'.

像这样,例如:

from typing import NamedTuple

class Wharrgarbl(NamedTuple):
    a: str
    b: str
    c: str
    sum: int = 6
    version: str = 'old'
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

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
Run Code Online (Sandbox Code Playgroud)

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

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

方案3: 实施所述映射协议.这允许按键访问行为,在__iter__不使用的情况下转换为a dict(),并且还提供关键字解包行为.

映射协议要求您(至少)提供两种方法:(key, value)dict.

def __iter__(self):
    yield 'a', self.a
    yield 'b', self.b
    yield 'c', self.c
Run Code Online (Sandbox Code Playgroud)

现在你可以做以下事情:

dict(my_object)
Run Code Online (Sandbox Code Playgroud)

请注意,映射协议优先于直接list(my_object)将对象转换为方法obj["a"](不使用kwarg解包,即dict).因此,当用作可迭代(例如,__iter__)与转换为{**my_obj}(dict(**my_obj))时,使对象具有不同的行为是可能的 - 有时是方便的.

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

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

选项4:考虑使用keys()'模块.

继承您的类__getitem____iter__向其他用户发出信号,为了所有意图和目的,您的类是映射*并且可以期望以这种方式运行.

您仍然可以dict根据需要将对象转换为对象,但可能没有理由这样做.由于鸭子打字,打扰你的映射对象,dict(m)在大多数时候只是一个额外的不必要的步骤.

这个答案可能也会有所帮助.

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

还要考虑查看'collections.abc.Mapping模块中的数字abcs :

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

既然你也将你的对象转换为a 'collections.abc.MutableMapping,那么将你的类变成一个完全成熟的类可能更有意义,dict因此不需要进行转换.

选项5:查看使用该dict模块(仅限Python 3.7).

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

现在你可以这样做:

>>> 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'}
Run Code Online (Sandbox Code Playgroud)

*"映射"已成为dict类似鸭子类型的标准"名称"


jpm*_*c26 17

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

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


Gar*_*t R 11

我认为这对你有用.

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)
Run Code Online (Sandbox Code Playgroud)

产量

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


Jor*_*ley 10

像这样的东西可能会奏效

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)
Run Code Online (Sandbox Code Playgroud)


tvt*_*173 7

像许多其他人一样,我建议实现一个 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
Run Code Online (Sandbox Code Playgroud)