使类可同时应用于元组和字典

时间:2018-07-23 18:12:10

标签: python python-2.7 dictionary tuples iterable

我想定义一个类,以便可以将其实例强制转换为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,如果它产生双精度值,则可以强制转换为{{1 }}:

dict

有什么方法可以使实例在Python 2.7中可同时转换为 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 tuple

2 个答案:

答案 0 :(得分:6)

您可以将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有 any 注意,无论如何,命名元组应该是可以接受的。我会尝试的!

如果要使用字典,我建议使用显式的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)

答案 1 :(得分:3)

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

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


我的建议是:

  1. 按照其他答案的建议进行操作,并利用namedtuple的{​​{1}}方法“免费”。
  2. 使用mapping protocol来实现该类(如上面的上一链接所述)。如果这样做,可以强制转换为_asdict()时完全规避__iter__方法。

您可能会这样做;但这有点奇怪:

dict

通过上述操作,使用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}

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

dataclasses

像这样使用它:

from dataclasses import dataclass, asdict, astuple

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