在你反对之前:每个程序员都很懒惰。否则你不会手动编程并做任何事情!
我有一个类Line
,它包含处理一条线所需的一切(比如使用两个顶点/点创建的对象)。这个类实际上非常复杂,为了简单起见,可维护性和清晰度,我想保持这样:我用两个顶点提供的类,输出一些困难的结果,比如两点之间的距离。
现在,问题在于,虽然我需要跟踪这些单独的行,但我有时也希望将它们作为一个整体来处理。例如,我想计算由许多行组成的路径的长度。
我创建了一个名为Lines
的类,它也提供了一些方法。
Lines
目前是numpy.ndarray
的孩子,并不是很好:
ufunc
来围绕Lines
的方法在Line
内提供一个包装器,但是将代码保存在两个这样的位置是很乏味的。那么,你们如何有效地“向量化”Line
类,同时跟踪各条线?
我可以将所有内容都放在Lines
中,并将Line
视为一个特殊情况,我尝试过,但它确实会损害清晰度,并使各个行的引用很难实现和维护。
import numpy as np
class Line:
def __init__ (self, input_points):
assert len(np.array(input_points).squeeze()) == 2
self._points = np.array(input_points)
def get_distance(self):
return np.sqrt(((self._points[0]-self._points[1])**2).sum())
from itertools import combinations
class Lines(np.ndarray):
_get_dists = np.frompyfunc(Line.get_distance, 1, 1)
def __new__(cls, data):
comb = [Line(el) for el in combinations(data, 2)]
obj = np.asarray(comb).view(cls)
obj = obj.squeeze()
return obj
def get_all_distances(self):
return self._get_dists(self)
答案 0 :(得分:3)
如果您希望Lines能够使用 ndarray
方法,但没有使用这些方法混淆其公共命名空间,请使用委托而不是继承。换句话说,而不是:
class Lines(np.ndarray):
def __init__(self, whatever):
super().__init__(stuff)
def dostuff(self, thingies):
np.do_thingy(self.stuff(spam))
return self.spam(eggs)
......这样做:
class Lines(object):
def __init__(self, whatever):
self.lines = np.array(stuff)
def dostuff(self, thingies):
np.do_thingy(self.lines.stuff(spam))
return self.lines.spam(eggs)
同时,听起来你有一套Line
方法需要在Lines
中使用ufuncify,而你却厌倦了重复自己。所以动态地这样做。这是一个简单的例子,可以给你一个想法:
for name in 'bam', 'biff', 'pow', 'kazaam':
func = getattr(Line, name)
ufunc = np.frompyfunc(func, 1, 1)
setattr(Lines, name, ufunc)
答案 1 :(得分:1)
[写完这个答案之后我看到@abarnert给出了同样的答案,但这个答案看起来不一样,所以我发布它,以防它有帮助]
您可以显式地包装您需要的每个方法和属性(对方法使用一个泛型包装函数,为属性使用一个),并手动将包装结果分配给Lines
类:
class Line(object):
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def diff(self):
return self.p2 - self.p1
@property
def point1(self):
return self.p1
class Lines(object):
def __init__(self, lines):
self._lines = np.array(lines, dtype = object)
def _wrapped_method(mname):
def f(self, *args, **kwargs):
return np.array([ getattr(line, mname)(*args, **kwargs) for line in self._lines ])
return f
def _wrapped_property(pname):
def f(self):
return np.array([ getattr(line, pname) for line in self._lines ])
return property(f)
wrapped_methods = ( 'diff', )
for mname in wrapped_methods:
setattr(Lines, mname, _wrapped_method(mname))
wrapped_properties = ( 'point1', )
for pname in wrapped_properties:
setattr(Lines, pname, _wrapped_property(pname))
lines = Lines([ Line(3,5) ]) # 3,5 are not really points, but good enough for demonstration
print '%r' % lines.diff()
# array([2])
print '%r' % lines.point1
# array([3])