Pythonzip函数(在某种程度上)是它自己的逆函数,因此我们可以这样做:
points = [(1,2), (3,4), (5,6), (7,8)]
xs, ys = zip(*points)
Run Code Online (Sandbox Code Playgroud)
现在xs=[1,3,5,7]和ys=[2,4,6,8]。
我想知道是否可以使用数据类实例而不是元组来完成类似的操作:
from dataclasses import dataclass
@dataclass
class XY:
"2d point"
x: float | int
y: float | int
points = [XY(1,2), XY(3,4), XY(5,6), XY(7,8)]
xs, ys = zip(*[(p.x,p.y) for p in points])
Run Code Online (Sandbox Code Playgroud)
但没有明确的列表理解。
当然,结果不会是一个元组,(xs,ys)而是一个带有键的字典x
,y因为如果没有显式的列表理解,我们将收集
所有字段。
And*_*ely 37
您可以__iter__在数据类中定义自定义魔术函数:
from dataclasses import dataclass
@dataclass
class XY:
"2d point"
x: float | int
y: float | int
def __iter__(self):
yield self.x
yield self.y
points = [XY(1,2), XY(3,4), XY(5,6), XY(7,8)]
xs, ys = zip(*points)
print(xs)
print(ys)
Run Code Online (Sandbox Code Playgroud)
印刷:
(1, 3, 5, 7)
(2, 4, 6, 8)
Run Code Online (Sandbox Code Playgroud)
Kel*_*ndy 21
和astuple:
from dataclasses import dataclass, astuple
@dataclass
class XY:
"2d point"
x: float | int
y: float | int
def __iter__(self):
return iter(astuple(self))
points = [XY(1,2), XY(3,4), XY(5,6), XY(7,8)]
xs, ys = zip(*points)
Run Code Online (Sandbox Code Playgroud)
或者改为映射它:
xs, ys = zip(*map(astuple, points))
Run Code Online (Sandbox Code Playgroud)
Hai*_* Vu 10
如果您正在寻找使用的解决方案zip,其他人已经回答了。但是,如果您正在寻找最终结果,那就是xs和ys变量。您可以只使用列表理解:
xs = [point.x for point in points]
ys = [point.y for point in points]
Run Code Online (Sandbox Code Playgroud)
这比尝试使用 zip 的其他解决方案更简单、更容易理解。
回应 sds 的评论:是的,需要两行代码,但是
为了演示速度,我将我的解决方案与astuple实际解决方案进行了比较:
from dataclasses import dataclass, astuple
import timeit
@dataclass
class XY:
"""2d point"""
x: float | int
y: float | int
def __iter__(self):
return iter(astuple(self))
def my_method(points):
xs = [point.x for point in points]
ys = [point.y for point in points]
return xs, ys
def astuple_method(points):
xs, ys = zip(*points)
return xs, ys
points = [XY(1, 2), XY(3, 4), XY(5, 6), XY(7, 8)]
# My time
my_time = timeit.timeit(
stmt="my_method(points)",
globals=globals(),
)
print(f"\n# my_method: {my_time}")
# astuple time
astuple_time = timeit.timeit(
stmt="astuple_method(points)",
globals=globals(),
)
print(f"# astuple_method: {astuple_time}")
print(f"# Ratio astuple:my: {astuple_time / my_time}")
Run Code Online (Sandbox Code Playgroud)
下面是一个示例输出,它表明我的解决方案快了大约 20 倍:
# my_method: 0.42180337477475405
# astuple_method: 8.835096125025302
# Ratio astuple:my: 20.9460062517122
Run Code Online (Sandbox Code Playgroud)
如果您尝试使用类来执行此操作,您会得到提示:TypeError: 'XY' object is not iterable。
使类可迭代(add __iter__):
from dataclasses import dataclass, fields
@dataclass()
class XY:
"2d point"
x: float | int
y: float | int
def __iter__(self):
return (getattr(self, field.name) for field in fields(self))
Run Code Online (Sandbox Code Playgroud)
现在:
points = [XY(1, 2), XY(3, 4), XY(5, 6), XY(7, 8)]
xs, ys = zip(*points)
xs,ys # ((1, 3, 5, 7), (2, 4, 6, 8))
Run Code Online (Sandbox Code Playgroud)
一种丑陋的解决方案,但不需要您修改现有的类,是提供一个自定义函数,将实例转换为可迭代对象,然后将其应用到map.
例如:
def pt2iter(pt):
yield pt.x
yield pt.y
xs, ys = zip(*map(pt2iter, points))
Run Code Online (Sandbox Code Playgroud)
可迭代对象可以是任何东西。例如
def pt2iter(pt):
return pt.x, pt.y
Run Code Online (Sandbox Code Playgroud)