如果我做对了,您只想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__
方法编写一个类 - 并让 getitem 返回的对象具有这两个属性。
为此,您可以使用一个更简单的类来表示您的标量,并且根据您的需要,这个更简单的类可以是一个命名元组:
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__
允许 PythonFoo
在 for 循环迭代中自动使用的实例相结合)
现在,如果你想让它变得非常有趣,让我们假设你的Foo
类,在你的问题中存在转换的标量应用程序 - 可以“转换”它,以便它可以使用序列操作。
比我们更接近原始metaclass
研究——并且可以通过使用类装饰器来实现。很久以前就引入了类装饰器,以取代元类的某些用途。
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
上面的“工厂”函数可以制成 a__new__
并插入到装饰类中,然后生成的装饰类将表现为真正的类 - 但特别是如果您有内省代码并且需要Foo
该类是在标量上运行的真实类,你最好有两个单独的类——一个创建序列,另一个处理标量。
在这种情况下,您可以剥离“工厂”功能,让“自动序列”返回 AutoSequence 类本身,并像这样使用它:
class Foo:
...
FooSequence = autosequence(Foo)