使类可转换为元组和字典
Posted
技术标签:
【中文标题】使类可转换为元组和字典【英文标题】:Make class castable to both tuple and dict 【发布时间】:2018-12-31 06:26:27 【问题描述】:我想定义一个类,以便它的实例可以同时转换为tuple
和dict
。一个例子:
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 中,有什么方法可以使实例同时转换为 tuple
和 dict
?
【问题讨论】:
为什么不在你的类上定义名为.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.coordinates
或 p.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 已修复。以上是关于使类可转换为元组和字典的主要内容,如果未能解决你的问题,请参考以下文章