使类可转换为元组和字典

Posted

技术标签:

【中文标题】使类可转换为元组和字典【英文标题】:Make class castable to both tuple and dict 【发布时间】:2018-12-31 06:26:27 【问题描述】:

我想定义一个类,以便它的实例可以同时转换为tupledict。一个例子:

class Point3:
    ...

p = Point(12, 34, 56)

tuple(p)  # gives (12, 34, 56)
dict(p)   # gives  'x': 12, 'y': 34, 'z': 56 

我发现,如果我将 __iter__ 定义为产生单个值的迭代器,则可以将实例转换为 tuple,如果它产生双值,则可以将其转换为 dict

class Point3:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    # This way makes instance castable to tuple
    def __iter__(self):
        yield self.x
        yield self.y
        yield self.z

    # This way makes instance castable to dict
    def __iter__(self):
        yield 'x', self.x
        yield 'y', self.y
        yield 'z', self.z

在 Python 2.7 中,有什么方法可以使实例同时转换为 tupledict

【问题讨论】:

为什么不在你的类上定义名为 .to_tuple().to_dict() 的方法,以任何你想要的方式转换为元组和字典? Python 中没有“类型转换”。无论如何,您为什么要将对象转换为普通的字典或列表?只需实现您想要的访问方法并直接使用它们。 @BrenBarn。这就是我现在解决它的一种方式。我已将类“castable”为tuple 并使用.to_dict() 创建字典。 @md2perpe 这是一个很好的问题,但是您需要在某个地方进行切换。您何时要将其转换为字典,何时将其转换为元组(因为两者都调用 __iter__)。 @md2perpe:如果是这样,那么该工具已严重损坏。从dict(或namedtuple)继承Point,并在必要时使用tuple(p.items()) 【参考方案1】:

您可以只继承 NamedTuple(其他类型可用,请咨询您的医生。):

from typing import NamedTuple


class Point(NamedTuple):
    x: float
    y: float
    z: float

    def __add__(self, p):
        return Point(self.x+p.x, self.y+p.y, self.z+p.z)


p = Point(1, 2, 3)
q = Point(5, 5, 5)

print(p.x, p.y, p.z)
print(p+q)
print(tuple(p))

.

$ python pointless.py
1 2 3
Point(x=6, y=7, z=8)
(1, 2, 3)

如果您使用的工具任何考虑到惯用的 Python,那么命名元组应该是可以接受的,无论如何。我会试试的!

如果您要使用字典,我建议您使用显式的 tuple(p.values())(子类化时)或 p.coordinatesp.xyz 作为属性(包装时),而不是依赖于幕后的一些魔术。


旧版,无保修。

from collections import namedtuple


_Point = namedtuple('Point', 'x y z')


class Point(_Point):
    __slots__ = ()

    def __add__(self, p):
        return Point(self.x+p.x, self.y+p.y, self.z+p.z)

【讨论】:

x : float 上的语法错误。报告的 Python 版本:2.7.11。 注意:从 3.7 开始,如果您需要一个可变类,the dataclasses module 基本上是typing.NamedTuple 的升级版,但默认情况下具有可变性,并且具有更大的可配置性。【参考方案2】:

你不能做你最初想做的事情(即,有两个不同的__iter__ 方法),因为它没有任何意义。但是你可以使用映射协议来伪造它(见下文)。

但在开始之前,如果您真的想要转换为 dict、have a look at this answer 以获得更好的选择。


我的建议是:

    按照其他答案中的建议进行操作并使用namedtuple,它会“免费”为您提供_asdict() 方法。 使用mapping protocol 实现类(如上面的上一个链接中所述)。如果这样做,则可以在转换为 dict 时完全绕过 __iter__ 方法。

你可以这样做;然而这有点奇怪:

class Point3:
    _fields = tuple("xyz")
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
    def __iter__(self):
        for f in self._fields:
            yield getattr(self, f)
    def keys(self):
        return self._fields
    def __getitem__(self, i):
        if i in self._fields:
            return getattr(self, i)
        raise KeyError("!r is not a valid field".format(i))

通过上述方法,dict 是使用keys()__getitem__() 而不是__iter__ 创建的:

>>> dict(Point3(1, 2, 3))
'x': 1, 'y': 2, 'z': 3

使用映射协议也可以派上用场,因为您可以“强制转换”(即以关键字参数的形式将对象解包)到接受与关键字参数相同的字段名称的任何其他类型,例如:

point= XYZer(**point3_instance)

对于能够从最新版本的 Python 3 (3.7) 中受益的其他人(不是 OP):我强烈建议使用 dataclasses 模块:

from dataclasses import dataclass, asdict, astuple

@dataclass
class Point:
    x: float
    y: float
    z: float

像这样使用它:

>>> p = Point(1,2,3)
>>> asdict(p)
'x': 1, 'y': 2, 'z': 3
>>> astuple(p)
(1, 2, 3)

【讨论】:

至第 2 点,您不需要使用 dict 构造函数的解包语法;你可以做dict(point)dict(**point) 有点多余(它转换为dict 以解包,然后读取结果以初始化新的dict)。 @ShadowRanger 这是一个很好的观点,我忘记了映射协议短路__iter__ @ShadowRanger 已修复。

以上是关于使类可转换为元组和字典的主要内容,如果未能解决你的问题,请参考以下文章

Python将字典中的值转换为元组

将嵌套列表的字典转换为元组列表

将数据框转换为元组列表字典

使用 pig,如何将混合格式的行解析为元组和一袋元组?

Python - 列表与字典相互转换

为什么将Python dict转换为元组会返回一组键?