很难用抽象的方式来描述这个,所以让我给出一个(简化的& snipped)示例:
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)
但这感觉更加苛刻。
我也可以将每个方法变成一个普通超类的实例,但这感觉就像是矫枉过正。
答案 0 :(得分: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
在其他一些语言中,可能会使用command pattern之类的设计模式,但这主要是因为这些语言没有第一类函数/方法对象。 Python内置了这种模式。
答案 1 :(得分:1)
您可以使用类装饰器生成指标列表
方法。这样做的好处是你可以生成
度量方法列表在类定义时间而不是
每次调用 printResults
时重新生成列表。
另一个优点是您不必手动维护ClassificationResults.metrics
列表。您不必在两个地方拼写方法的名称,因此它是DRY-er,如果您添加了另一个指标,则无需记住同时更新ClassificationResults.metrics
。您只需要为其命名,以metrics_
开头。
由于每个度量方法都返回一个类似的对象,您可以考虑
在课堂上形成这种概念(例如下面的Metric
)。一
这样做的好处是您可以定义__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()
答案 2 :(得分:0)
所以你基本上想要消除getattr调用以及在两个地方指定函数的需要。或命令模式。
对于增强的可调用类似乎是一个体面的案例,可能是这样的:
class Command(object):
def __init__(self, function=None):
self._function = function
def function(self, *args):
return self._function(*args)
def name(self):
return self.function.func_name # Or other code for callables.
def __call__(self, *args):
return self.function(*args)
然后是一些组合:
commands = []
def recall(my, args):
...
commands.append(Command(recall))
class Precision(Command):
def function(self, my, args):
...
commands.append(Precision)
然后
results = [command() for command in commands]
或者
results = [(command.name(), command() for command in commands)]
或跑步者:
class Runner(object):
def __init__(self, commands):
groupings = {}
for command in commands:
groupings.setdefault(command.__class__.__name__, []).append(command)
self.groupings = groupings
def run(self, group=None):
commands = self.groupings.get(group,[]) if group else itertools.chain(*self.groupings.values())
return [command() for command in commands]
Yadda yadda yadda。
快速编写此代码,因此可能会有一两个错字。
亚当