5

在你反对之前:每个程序员都是懒惰的。否则,您将不会手动编程和执行所有操作!


一个简单的例子。

我有一个类Line,其中包含处理一条线所需的所有内容(例如使用两个顶点/点创建的对象)。这个类实际上非常复杂,为了简单、可维护性和清晰起见,我想保持这样:我用两个顶点提供一个类,并输出一些困难的结果,例如两点之间的距离。

问题

现在,问题是,虽然我需要跟踪这些单独的行,但有时我还想将它们作为一个整体来处理。例如,我想计算由多条线组成的路径的长度。

目前的解决方案和缺点

我创建了一个名为的类Lines,它也为此提供了一些方法。

Lines目前是一个numpy.ndarray不那么伟大的孩子:

  • 命名空间被 ndarray 的方法弄乱了;
  • 我正在使用s在方法中ufunc提供一个包装器,但是在这样的两个地方维护代码是很乏味的。LinesLine

问题

那么,你们将如何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)
4

2 回答 2

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你想要 ufuncify in 的方法Lines,而且你厌倦了重复自己。所以动态地这样做。这是一个简单的示例,可以为您提供这个想法:

for name in 'bam', 'biff', 'pow', 'kazaam':
    func = getattr(Line, name)
    ufunc = np.frompyfunc(func, 1, 1)
    setattr(Lines, name, ufunc)
于 2013-10-25T18:34:58.783 回答
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])
于 2013-10-25T18:52:02.880 回答