class 的索引成员作为 python 中的列表
Index member of class as list in python
假设我有一个简单的class像
class Foo:
def __init__(bar):
self.x = transform1(bar)
self.y = transform2(bar)
我现在对生成一个 class 感兴趣,我可以在其中将 bar
的可迭代对象传递给初始化程序并取回 Foo
的实例,我可以在其中访问成员 x
和 y
喜欢大小为 bar
的迭代器,即
x = [1, 2, 3]
foo = Foo(x)
plt.plot(foo.x, foo.y)
我知道很容易做到
foo = [Foo(elem) for elem in x]
plt.plot([elem.x for elem in foo], [elem.y for elem in foo])
但这感觉很冗长而且可能不是很有效。我可以粗略地想象一个带有 numpy 结构化数组的解决方案,但我只是好奇是否有任何标准解决方案。也许使用 metaclasses。谷歌搜索主要是关于如何获取 class 或类似的所有成员列表的结果。
如果有人甚至可以提出一种解决方案,允许索引 对象 foo
或 其成员,这将是伟大的。
您可以用一个循环而不是 2 个循环来完成:
class Foo:
def __init__(self, bar):
self.xs = []
self.ys = []
for elem in bar:
self.xs.append(elem.x)
self.ys.append(elem.y)
您可以使用 map
和 zip
隐藏循环:
class Foo:
def __init__(self, bar):
self.xs, self.ys = zip(*map(lambda e: (e.x, e.y), bar))
如果我没看错,您只想一次转换 bar
中的所有元素。
只做它,而不是一次一个标量。就去做吧:
class Foo:
def __init__(bar):
self.x = [transform1(el) for el in bar]
self.y = [transform2(el) for el in bar]
真的就这么简单。如果您想使用线程或进程并行 运行 transform1 和 transform2,或者如果您希望以惰性方式根据需要计算所有变换,则有一些奇特的东西。
但是要绘制图形,列表就可以了。在单个 for
循环而不是两个列表推导中完成它甚至没有任何好处——使用 for
本身的迭代所花费的时间可以忽略不计。
如果您希望能够索引实例本身,并取回具有所需属性的对象,则必须使用 __getitem__
方法编写 class - 并具有由 getitem return编辑的对象具有两个属性。
为此你可以使用一个更简单的 class,代表你的标量,并且根据你的需要,这个更简单的 class 可以是一个命名元组:
from collections import namdtuple
ScalarFoo = namedtuple("ScalarFoo", "x y")
class Foo:
def __init__(bar):
self.x = [transform1(el) for el in bar]
self.y = [transform2(el) for el in bar]
def __getitem__(self, index):
return ScalarFoo(self.x[index], self.y[index])
def __len__(self):
return len(self.x)
(__len__
方法结合 __getitem__
允许 Python 在 for 循环迭代中自动使用 Foo
的实例)
现在,如果你想让它变得非常有趣,让我们假设你的 Foo
class,在你的问题中存在变换的标量应用 - 可以“变换”以便它可以使用序列进行操作。
比我们更接近最初的 metaclass
研究 - 并且可以通过使用 class 装饰器来实现。 Class 装饰器在很久以前就被引入以取代 metaclasses 的一些用途。
def autosequence(cls):
"""Transforms the received class into a factory,
so that if a sequence or iterator is passed as the first
argument to it, a new, sequence class is used. If the
resulting class is used in an iteration or as a sequence,
an instance of the original class is returned
"""
class AutoSequence:
def __init__(self, *args, **kw):
self.sequence = list(args[0])
self.other_args = args[1:]
self.kw = kw
def __getitem__(self, index):
return cls(self.sequence[index], *self.other_args, **self.kw)
def __len__(self):
return len(self.sequence)
def __repr__(self):
return f"Lazy sequence of f{cls.__name__} objects with {len(self)} elements"
def factory(*args, **kw):
if args and hasattr(args[0], "__len__") or hasattr(args[0], "__iter__"):
return AutoSequence(*args, **kw)
return cls(*args, **kw)
factory.__name__ = cls.__name__
return factory
def transform1(a):
return a
def transform2(a):
return a ** 2
@autosequence
class Foo:
def __init__(self, bar):
self.x = transform1(bar)
self.y = transform2(bar)
def __repr__(self):
return f"{self.__class__.__name__}({self.x}, {self.y})"
下面是它在交互式解释器中的表现:
In [24]: a = Foo([1,2,3])
In [25]: a[2]
Out[25]: Foo(3, 9)
In [26]: Foo(4)
Out[26]: Foo(4, 16)
In [27]: Foo(4).y
Out[27]: 16
In [28]: a[2].y
Out[28]: 9
上面的“factory”函数可以做成一个 __new__
并插入到修饰的 class 中,然后得到的修饰的 class 将表现得像真正的 [=50] =] - 但特别是如果你有内省代码并且需要 Foo
class 是真实的 class 在标量上运行,你最好有两个单独的 classes - 一个创建序列,另一个处理标量。
在那种情况下,您可以剥离“工厂”功能,将“自动序列”设置为 return 自动序列 class 本身,然后像这样使用它:
class Foo:
...
FooSequence = autosequence(Foo)
假设我有一个简单的class像
class Foo:
def __init__(bar):
self.x = transform1(bar)
self.y = transform2(bar)
我现在对生成一个 class 感兴趣,我可以在其中将 bar
的可迭代对象传递给初始化程序并取回 Foo
的实例,我可以在其中访问成员 x
和 y
喜欢大小为 bar
的迭代器,即
x = [1, 2, 3]
foo = Foo(x)
plt.plot(foo.x, foo.y)
我知道很容易做到
foo = [Foo(elem) for elem in x]
plt.plot([elem.x for elem in foo], [elem.y for elem in foo])
但这感觉很冗长而且可能不是很有效。我可以粗略地想象一个带有 numpy 结构化数组的解决方案,但我只是好奇是否有任何标准解决方案。也许使用 metaclasses。谷歌搜索主要是关于如何获取 class 或类似的所有成员列表的结果。
如果有人甚至可以提出一种解决方案,允许索引 对象 foo
或 其成员,这将是伟大的。
您可以用一个循环而不是 2 个循环来完成:
class Foo:
def __init__(self, bar):
self.xs = []
self.ys = []
for elem in bar:
self.xs.append(elem.x)
self.ys.append(elem.y)
您可以使用 map
和 zip
隐藏循环:
class Foo:
def __init__(self, bar):
self.xs, self.ys = zip(*map(lambda e: (e.x, e.y), bar))
如果我没看错,您只想一次转换 bar
中的所有元素。
只做它,而不是一次一个标量。就去做吧:
class Foo:
def __init__(bar):
self.x = [transform1(el) for el in bar]
self.y = [transform2(el) for el in bar]
真的就这么简单。如果您想使用线程或进程并行 运行 transform1 和 transform2,或者如果您希望以惰性方式根据需要计算所有变换,则有一些奇特的东西。
但是要绘制图形,列表就可以了。在单个 for
循环而不是两个列表推导中完成它甚至没有任何好处——使用 for
本身的迭代所花费的时间可以忽略不计。
如果您希望能够索引实例本身,并取回具有所需属性的对象,则必须使用 __getitem__
方法编写 class - 并具有由 getitem return编辑的对象具有两个属性。
为此你可以使用一个更简单的 class,代表你的标量,并且根据你的需要,这个更简单的 class 可以是一个命名元组:
from collections import namdtuple
ScalarFoo = namedtuple("ScalarFoo", "x y")
class Foo:
def __init__(bar):
self.x = [transform1(el) for el in bar]
self.y = [transform2(el) for el in bar]
def __getitem__(self, index):
return ScalarFoo(self.x[index], self.y[index])
def __len__(self):
return len(self.x)
(__len__
方法结合 __getitem__
允许 Python 在 for 循环迭代中自动使用 Foo
的实例)
现在,如果你想让它变得非常有趣,让我们假设你的 Foo
class,在你的问题中存在变换的标量应用 - 可以“变换”以便它可以使用序列进行操作。
比我们更接近最初的 metaclass
研究 - 并且可以通过使用 class 装饰器来实现。 Class 装饰器在很久以前就被引入以取代 metaclasses 的一些用途。
def autosequence(cls):
"""Transforms the received class into a factory,
so that if a sequence or iterator is passed as the first
argument to it, a new, sequence class is used. If the
resulting class is used in an iteration or as a sequence,
an instance of the original class is returned
"""
class AutoSequence:
def __init__(self, *args, **kw):
self.sequence = list(args[0])
self.other_args = args[1:]
self.kw = kw
def __getitem__(self, index):
return cls(self.sequence[index], *self.other_args, **self.kw)
def __len__(self):
return len(self.sequence)
def __repr__(self):
return f"Lazy sequence of f{cls.__name__} objects with {len(self)} elements"
def factory(*args, **kw):
if args and hasattr(args[0], "__len__") or hasattr(args[0], "__iter__"):
return AutoSequence(*args, **kw)
return cls(*args, **kw)
factory.__name__ = cls.__name__
return factory
def transform1(a):
return a
def transform2(a):
return a ** 2
@autosequence
class Foo:
def __init__(self, bar):
self.x = transform1(bar)
self.y = transform2(bar)
def __repr__(self):
return f"{self.__class__.__name__}({self.x}, {self.y})"
下面是它在交互式解释器中的表现:
In [24]: a = Foo([1,2,3])
In [25]: a[2]
Out[25]: Foo(3, 9)
In [26]: Foo(4)
Out[26]: Foo(4, 16)
In [27]: Foo(4).y
Out[27]: 16
In [28]: a[2].y
Out[28]: 9
上面的“factory”函数可以做成一个 __new__
并插入到修饰的 class 中,然后得到的修饰的 class 将表现得像真正的 [=50] =] - 但特别是如果你有内省代码并且需要 Foo
class 是真实的 class 在标量上运行,你最好有两个单独的 classes - 一个创建序列,另一个处理标量。
在那种情况下,您可以剥离“工厂”功能,将“自动序列”设置为 return 自动序列 class 本身,然后像这样使用它:
class Foo:
...
FooSequence = autosequence(Foo)