0

これを抽象的に説明するのは難しいので、(簡略化して抜粋した)例を挙げましょう。

class ClassificationResults(object):

  #####################################################################################################################
  # These methods all represent aggregate metrics. They all follow the same interface: they return a tuple
  # consisting of the numerator and denominator of a fraction, and a format string that describes the result in terms
  # of that numerator, denominator, and the fraction itself.
  #####################################################################################################################
  metrics  = ['recall', 'precision', 'fmeasure', 'f2measure', 'accuracy']

  # ...

  def recall(self):
    tpos, pos = 0, 0
    for prediction in self.predictions:
      if prediction.predicted_label == 1:
        pos += 1
        if prediction.true_label == 1:
          tpos += 1
    return tpos, pos, "{1} instances labelled positive. {0} of them correct (recall={2:.2})"

  def precision(self):
    tpos, true = 0, 0
    for prediction in self.predictions:
      if prediction.true_label == 1:
        true += 1
        if prediction.predicted_label == 1:
          tpos += 1
    return tpos, true, "{1} positive instances. We labelled {0} correctly (precision={2:.2})"

  # ...

  def printResults(self):
    for methodname in self.metrics:
      (num, denom, msg) = getattr(self, methodname)()
      dec = num/float(denom)
      print msg.format(num, denom, dec)

これらのメソッドがすべて同じ「ファミリー」に属していることを示し、毎回名前を付けずにループで呼び出せるようにするより良い方法はありますか?

私が過去に行った別の方法は、メソッドに共通のプレフィックスを付けることです。

  def metric_precision(self):
    tpos, true = 0, 0
    for prediction in self.predictions:
      if prediction.true_label == 1:
        true += 1
        if prediction.predicted_label == 1:
          tpos += 1
    return tpos, true, "{1} positive instances. We labelled {0} correctly (precision={2:.2})"

  # ...

  def printResults(self):
    for methodname in dir(self):
      meth = getattr(self, methodname)
      if methodname.startswith('metric_') and callable(meth): 
        (num, denom, msg) = getattr(self, methodname)()
        dec = num/float(denom)
        print msg.format(num, denom, dec)

しかし、これはさらにハックな気がします。

各メソッドを共通のスーパークラスのインスタンスにすることもできますが、これはやり過ぎのように感じます。

4

3 に答える 3

2

getattr実際のメソッドをリストに保存して、呼び出しを完全に避けてみませんか?

>>> class SomeClass(object):
...     
...     def method_one(self):
...         print("First!")
...         return 0
...     
...     def method_two(self):
...         print("Second!")
...         return 1
...     
...     def method_three(self):
...         print("Third!")
...         return 2
...     
...     _METHODS = (method_one, method_two, method_three)
...     
...     def call_all(self):
...         for method in SomeClass._METHODS:
...             # remember that _METHODS contains *unbound* methods! 
...             print("Result: {}".format(method(self)))
... 
>>> obj = SomeClass()
>>> obj.call_all()
First!
Result: 0
Second!
Result: 1
Third!
Result: 2

他のいくつかの言語では、コマンドパターンのようなデザイン パターンが使用される場合がありますが、これは主に、これらの言語がファースト クラスの関数/メソッド オブジェクトを持たないためです。Python には、この種のパターンが組み込まれています。

于 2013-05-31T20:36:56.743 に答える
1
  • クラス デコレーターを使用して、メトリック メソッドのリストを生成できます。これを行う利点は、呼び出されるたびにリストを再生成するのではなく、クラス定義時にメトリック メソッドのリストを生成できることです。 printResults

    もう 1 つの利点は、リストを手動で保守する必要がないことですClassificationResults.metrics。メソッドの名前を 2 か所で綴る必要がないため、DRY-erであり、別のメトリックを追加する場合は、忘れずに も更新する必要はありませんClassificationResults.metrics。で始まる名前を付けるだけmetrics_です。

  • 各メトリクス メソッドは同様のオブジェクトを返すため、その概念をクラスで形式化することを検討してください (Metric以下の など)。これを行う利点の 1 つは__repr__、結果の出力方法を処理するメソッドを定義できることです。printResults (以下) がどれほど単純になるかに注目してください。


def register_metrics(cls):
    for methodname in dir(cls):
        if methodname.startswith('metric_'):
            method = getattr(cls, methodname)
            cls.metrics.append(method)
    return cls


class Metric(object):
    def __init__(self, pos, total):
        self.pos = pos
        self.total = total

    def __repr__(self):
        msg = "{p} instances labelled positive. {t} of them correct (recall={d:.2g})"
        dec = self.pos / float(self.total)
        return msg.format(p=self.total, t=self.pos, d=dec)


@register_metrics
class ClassificationResults(object):
    metrics = []

    def metric_recall(self):
        tpos, pos = 1, 2
        return Metric(tpos, pos)

    def metric_precision(self):
        tpos, true = 3, 4
        return Metric(tpos, true)

    def printResults(self):
        for method in self.metrics:
            print(method(self))

foo = ClassificationResults()
foo.printResults()

于 2013-05-31T20:56:24.143 に答える