5

異議を唱える前に: すべてのプログラマーは怠け者です。そうでなければ、すべてを手動でプログラムして実行することはできません!


簡単な例。

ラインを処理するために必要なすべてを含むクラスLineがあります (2 つの頂点/ポイントを使用して作成されたオブジェクトなど)。このクラスは実際には非常に複雑であり、シンプルさ、保守性、および明確さのために、このように保ちたいと思います: 2 つの頂点を入力し、2 点間の距離などの難しい結果を出力するクラスです。

問題

問題は、これらの個々の行を追跡する必要がある一方で、場合によってはそれらを全体として処理したいことです。たとえば、多くの線で構成されるパスの長さを計算したいと思います。

現在の解決策と欠点

Linesそのためのいくつかのメソッドも提供するという名前のクラスを作成しました。

Linesは現在、numpy.ndarrayあまり大きくない子です:

  • 名前空間は ndarray のメソッドによって雑然としています。
  • のメソッドの周りにufuncラッパーを提供するために s を使用していますが、このように 2 つの場所でコードを維持するのは面倒です。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)

一方、 でLineufuncify したい一連のメソッドがあり、同じこと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 が同じように回答しているのを見ましたが、この回答は異なっているように見えるので、役立つ場合に備えて投稿します]

必要な各メソッドとプロパティを明示的にラップし (メソッドには 1 つの汎用Linesラッピング関数を使用し、プロパティには 1 つの汎用ラッピング関数を使用)、ラップされた結果をクラスに手動で割り当てることができます。

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 に答える