admin管理员组文章数量:1401176
I'm using Python 3.12 and I would like to return polygon.exterior.coords
as a type of list[tuple[float, float]]
so that I can correctly enforce typing later in the program. I'm wondering if there is a more elegant solution:
from shapely import Polygon
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
data = polygon.exterior.coords
# This raises a run time error: IndexError: tuple index out of range
data[0][2]
I would like to do:
data: list[tuple[float, float]] = list(polygon.exterior.coords)
# This now correctly shows a type hint error: "Index 2 is out of range for type tuple[float, float]"
data[0][2]
However the line data: list[tuple[float, float]] = list(polygon.exterior.coords)
then shows a type hint error of:
Type "list[tuple[float, ...]]" is not assignable to declared type "list[tuple[float, float]]"
"list[tuple[float, ...]]" is not assignable to "list[tuple[float, float]]"
Type parameter "_T@list" is invariant, but "tuple[float, ...]" is not the same as "tuple[float, float]"
Consider switching from "list" to "Sequence" which is covariant
I can solve this by using this code:
data = type_safe_list(polygon.exterior.coords)
def type_safe_list(geometry: Polygon) -> list[tuple[float, float]]:
coords: list[tuple[float, float]] = []
for point in clipped_geometry.exterior.coords:
x, y = point
coords.append((x, y))
return coords
But perhaps there's a more elegant solution that doesn't incur a run time penalty? (not that performance is currently an issue for this part of the code). Also I'm actually trying to handle other types like MultiPolygon so the actual implementation I currently have is:
def type_safe_list(geometry: Polygon | MultiPolygon) -> list[tuple[float, float]] | list[list[tuple[float, float]]]:
if isinstance(geometry, Polygon):
coords: list[tuple[float, float]] = []
for point in geometry.exterior.coords:
x, y = point
coords.append((x, y))
return coords
elif isinstance(geometry, MultiPolygon):
multi_coords: list[list[tuple[float, float]]] = []
for poly in geometry.geoms:
coords: list[tuple[float, float]] = []
for point in poly.exterior.coords:
x, y = point
coords.append((x, y))
multi_coords.append(coords)
return multi_coords
else:
raise NotImplementedError(f"Unhandled type: {type(geometry)}")
return []
I'm using Python 3.12 and I would like to return polygon.exterior.coords
as a type of list[tuple[float, float]]
so that I can correctly enforce typing later in the program. I'm wondering if there is a more elegant solution:
from shapely import Polygon
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
data = polygon.exterior.coords
# This raises a run time error: IndexError: tuple index out of range
data[0][2]
I would like to do:
data: list[tuple[float, float]] = list(polygon.exterior.coords)
# This now correctly shows a type hint error: "Index 2 is out of range for type tuple[float, float]"
data[0][2]
However the line data: list[tuple[float, float]] = list(polygon.exterior.coords)
then shows a type hint error of:
Type "list[tuple[float, ...]]" is not assignable to declared type "list[tuple[float, float]]"
"list[tuple[float, ...]]" is not assignable to "list[tuple[float, float]]"
Type parameter "_T@list" is invariant, but "tuple[float, ...]" is not the same as "tuple[float, float]"
Consider switching from "list" to "Sequence" which is covariant
I can solve this by using this code:
data = type_safe_list(polygon.exterior.coords)
def type_safe_list(geometry: Polygon) -> list[tuple[float, float]]:
coords: list[tuple[float, float]] = []
for point in clipped_geometry.exterior.coords:
x, y = point
coords.append((x, y))
return coords
But perhaps there's a more elegant solution that doesn't incur a run time penalty? (not that performance is currently an issue for this part of the code). Also I'm actually trying to handle other types like MultiPolygon so the actual implementation I currently have is:
def type_safe_list(geometry: Polygon | MultiPolygon) -> list[tuple[float, float]] | list[list[tuple[float, float]]]:
if isinstance(geometry, Polygon):
coords: list[tuple[float, float]] = []
for point in geometry.exterior.coords:
x, y = point
coords.append((x, y))
return coords
elif isinstance(geometry, MultiPolygon):
multi_coords: list[list[tuple[float, float]]] = []
for poly in geometry.geoms:
coords: list[tuple[float, float]] = []
for point in poly.exterior.coords:
x, y = point
coords.append((x, y))
multi_coords.append(coords)
return multi_coords
else:
raise NotImplementedError(f"Unhandled type: {type(geometry)}")
return []
Share
Improve this question
edited Mar 25 at 14:11
InSync
11.1k4 gold badges18 silver badges56 bronze badges
asked Mar 25 at 14:07
AJPAJP
28.6k26 gold badges94 silver badges144 bronze badges
2 Answers
Reset to default 3The type of geometry.exterior.coords
is CoordinateSequence
, whose __iter__
only ever returns an interator of tuple[float, ...]
:
class CoordinateSequence:
...
def __iter__(self) -> Iterator[tuple[float, ...]]: ...
There is no way to change this other than editing the stubs of shapely
itself.
Thus, cast()
is probably your best bet:
def type_safe_list(geometry: Polygon) -> list[tuple[float, float]]:
return cast(list[tuple[float, float]], list(geometry.exterior.coords))
A Shapely polygon's vertices are allowed to have z coordinates. If the vertices have z coordinates, then the elements of polygon.exterior.coords
will be 3-element tuples.
This is why Shapely's type hints are the way they are, and why mypy rejects the assignment you want to perform: that assignment is not actually type-safe.
If you're sure you don't have z coordinates in your data, and you don't want to spend time checking, you can just typing.cast
it:
from typing import cast
data: list[tuple[float, float]] = cast(list[tuple[float, float]],
list(polygon.exterior.coords))
本文标签: Elegantly handling python type hint for Shapely polygonexteriorcoordsStack Overflow
版权声明:本文标题:Elegantly handling python type hint for Shapely polygon.exterior.coords - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744190891a2594511.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论