在 Python 类型中声明元组的长度
Declaring length of tuples in Python typing
我想知道是否要提交给元组[float, ...],即使我知道元组的长度。
我有一个 Point 和一个 Rect class,Rect class 中有一个 属性 aspoints
是 return 的一个元组左上角和右下角,作为二元组。
迭代器的类型为 Iterator[float]
,我知道它会给我两个浮点数。我希望 属性 的 return 值是
Tuple[Tuple[float, float], Tuple[float, float]]
因为我知道迭代器会为每个点提供两个浮点数。
我应该提交,只说它会return一个Tuple[Tuple[float, ...], Tuple[float, ...]]
,在文档中留下他们的长度的评论,或者有更好的解决方案吗?
这是代码。
from dataclasses import dataclass
from typing import Iterator, Tuple
@dataclass
class Point:
x: float
y: float
def __iter__(self) -> Iterator[float]:
return iter((self.x, self.y))
@dataclass
class Rect:
x: float
y: float
width: float
height: float
@property
def tl(self) -> Point:
return Point(self.x, self.y)
@property
def br(self) -> Point:
return Point(self.x + self.width, self.y + self.height)
@property
def aspoints(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
return tuple(self.tl), tuple(self.br)
问题出现在Rect.aspoints。从 MyPy 我得到以下错误:
error: Incompatible return value type (got "Tuple[Tuple[float, ...], Tuple[float, ...]]", expected "Tuple[Tuple[float, float], Tuple[float, float]]")
问题似乎是您对迭代器将 return 的元素数量的了解无法编码为静态类型。
我不确定重载 __iter__
以迭代固定数量的元素是最好的方法,因为您基本上是将它用作允许将对象转换为元组的技巧。
也许向点数据 class 添加类似 "to_tuple()" 方法的方法更有意义?您可以在那里声明类型...
编辑:或者,我猜你可以解构迭代器的输出,但你仍然没有节省很多代码:
a, b = self.tl
c, d = self.br
return (a, b), (c, d)
您可以扩展 aspoints
函数以正确转换字段类型:
def aspoints(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
left = cast(Tuple[float, float], tuple(self.tl))
right = cast(Tuple[float, float], tuple(self.br))
return left, right
您也可以将所有内容都放在一行中,但可读性会受到很大影响。 cast
函数在运行时不会做任何事情,它只是作为一种明确的方式告诉 mypy(或其他一些静态类型检查器)你对你的类型的了解比基本类型工具所能表达的更多。
绝对不要 将 __iter__
的 return 类型更改为任何不是迭代器的类型,那样会非常奇怪和令人困惑。
另一种方法:您真的不想迭代 Point
;你只是想要一种方法来获得 float
的元组,你只是让你的点可迭代,这样你就可以将它直接传递给 tuple
。 (考虑一下:你会写出看起来像 for coord in Point(3, 5): ...
的代码吗?)
而不是定义 __iter__
,而是定义一个函数来执行您真正想要的操作:returns 一对 float
s。
from typing import Tuple
PairOfFloats = Tuple[float,float]
@dataclass
class Point:
x: float
y: float
def as_cartesian_pair(self) -> PairOfFloats:
return (self.x, self.y)
@dataclass
class Rect:
x: float
y: float
width: float
height: float
@property
def tl(self) -> Point:
return Point(self.x, self.y)
@property
def br(self) -> Point:
return Point(self.x + self.width, self.y + self.height)
@property
def aspoints(self) -> Tuple[PairOfFloats, PairOfFloats]:
return self.tl.as_cartesian_pair(), self.br.as_cartesian_pair()
为了支持这种方法,它还允许您编写一个 附加 方法,该方法也是 returns 一对浮点数,但具有不同的语义:
def as_polar_pair(self) -> PairOfFloats:
return cmath.polar(complex(self.x, self.y))
关于解包,定义Point.__getitem__
而不是Point.__iter__
就足够了:
def __getitem__(self, i) -> float:
if i == 0:
return self.x
elif i == 1:
return self.y
else:
raise IndexError
>>> p = Point(3,5)
>>> p[0], p[1]
(3, 5)
>>> x, y = p
>>> x
3
>>> y
5
鉴于需求,似乎用typing.NamedTuple
作为Point
的基础class是最合适的。这将大大简化事情,因为正在使用 none 的 dataclasses.dataclass
功能,而且 Point
在给定的示例中似乎也不需要可变性。
from typing import Tuple, NamedTuple
from dataclasses import dataclass
class Point(NamedTuple):
x: float
y: float
@dataclass
class Rect:
...
@property
def aspoints(self) -> Tuple[Point, Point]:
return (self.tl, self.br)
尽管如此,似乎 Rect
也可能更合适地从 typing.NamedTuple
继承,原因与 Point
.
已经提到的相同
我想知道是否要提交给元组[float, ...],即使我知道元组的长度。
我有一个 Point 和一个 Rect class,Rect class 中有一个 属性 aspoints
是 return 的一个元组左上角和右下角,作为二元组。
迭代器的类型为 Iterator[float]
,我知道它会给我两个浮点数。我希望 属性 的 return 值是
Tuple[Tuple[float, float], Tuple[float, float]]
因为我知道迭代器会为每个点提供两个浮点数。
我应该提交,只说它会return一个Tuple[Tuple[float, ...], Tuple[float, ...]]
,在文档中留下他们的长度的评论,或者有更好的解决方案吗?
这是代码。
from dataclasses import dataclass
from typing import Iterator, Tuple
@dataclass
class Point:
x: float
y: float
def __iter__(self) -> Iterator[float]:
return iter((self.x, self.y))
@dataclass
class Rect:
x: float
y: float
width: float
height: float
@property
def tl(self) -> Point:
return Point(self.x, self.y)
@property
def br(self) -> Point:
return Point(self.x + self.width, self.y + self.height)
@property
def aspoints(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
return tuple(self.tl), tuple(self.br)
问题出现在Rect.aspoints。从 MyPy 我得到以下错误:
error: Incompatible return value type (got "Tuple[Tuple[float, ...], Tuple[float, ...]]", expected "Tuple[Tuple[float, float], Tuple[float, float]]")
问题似乎是您对迭代器将 return 的元素数量的了解无法编码为静态类型。
我不确定重载 __iter__
以迭代固定数量的元素是最好的方法,因为您基本上是将它用作允许将对象转换为元组的技巧。
也许向点数据 class 添加类似 "to_tuple()" 方法的方法更有意义?您可以在那里声明类型...
编辑:或者,我猜你可以解构迭代器的输出,但你仍然没有节省很多代码:
a, b = self.tl
c, d = self.br
return (a, b), (c, d)
您可以扩展 aspoints
函数以正确转换字段类型:
def aspoints(self) -> Tuple[Tuple[float, float], Tuple[float, float]]:
left = cast(Tuple[float, float], tuple(self.tl))
right = cast(Tuple[float, float], tuple(self.br))
return left, right
您也可以将所有内容都放在一行中,但可读性会受到很大影响。 cast
函数在运行时不会做任何事情,它只是作为一种明确的方式告诉 mypy(或其他一些静态类型检查器)你对你的类型的了解比基本类型工具所能表达的更多。
绝对不要 将 __iter__
的 return 类型更改为任何不是迭代器的类型,那样会非常奇怪和令人困惑。
另一种方法:您真的不想迭代 Point
;你只是想要一种方法来获得 float
的元组,你只是让你的点可迭代,这样你就可以将它直接传递给 tuple
。 (考虑一下:你会写出看起来像 for coord in Point(3, 5): ...
的代码吗?)
而不是定义 __iter__
,而是定义一个函数来执行您真正想要的操作:returns 一对 float
s。
from typing import Tuple
PairOfFloats = Tuple[float,float]
@dataclass
class Point:
x: float
y: float
def as_cartesian_pair(self) -> PairOfFloats:
return (self.x, self.y)
@dataclass
class Rect:
x: float
y: float
width: float
height: float
@property
def tl(self) -> Point:
return Point(self.x, self.y)
@property
def br(self) -> Point:
return Point(self.x + self.width, self.y + self.height)
@property
def aspoints(self) -> Tuple[PairOfFloats, PairOfFloats]:
return self.tl.as_cartesian_pair(), self.br.as_cartesian_pair()
为了支持这种方法,它还允许您编写一个 附加 方法,该方法也是 returns 一对浮点数,但具有不同的语义:
def as_polar_pair(self) -> PairOfFloats:
return cmath.polar(complex(self.x, self.y))
关于解包,定义Point.__getitem__
而不是Point.__iter__
就足够了:
def __getitem__(self, i) -> float:
if i == 0:
return self.x
elif i == 1:
return self.y
else:
raise IndexError
>>> p = Point(3,5)
>>> p[0], p[1]
(3, 5)
>>> x, y = p
>>> x
3
>>> y
5
鉴于需求,似乎用typing.NamedTuple
作为Point
的基础class是最合适的。这将大大简化事情,因为正在使用 none 的 dataclasses.dataclass
功能,而且 Point
在给定的示例中似乎也不需要可变性。
from typing import Tuple, NamedTuple
from dataclasses import dataclass
class Point(NamedTuple):
x: float
y: float
@dataclass
class Rect:
...
@property
def aspoints(self) -> Tuple[Point, Point]:
return (self.tl, self.br)
尽管如此,似乎 Rect
也可能更合适地从 typing.NamedTuple
继承,原因与 Point
.