统一相似的功能于一体

时间:2016-05-10 13:43:54

标签: python

我对生物数据进行了一些计算。每个函数计算一个对象列表的总值,平均值,最小值,最大值。 我的想法是,我有很多不同的列表,每个列表都是针对不同的对象类型。 我不想为每个函数重复我的代码只是更改“for”行和对象方法的调用!

例如:

音量功能:

def calculate_volume(self):
    total = 0
    min = sys.maxint
    max = -1
    compartments_counter = 0

    for n in self.nodes:

        compartments_counter += 1
        current = n.get_compartment_volume()
        if min > current:
            min = current
        if max < current:
            max = current

        total += current

    avg = float(total) / compartments_counter
    return total, avg, min, max

收缩功能:

def get_contraction(self):
    total = 0
    min = sys.maxint
    max = -1
    branches_count = self.branches.__len__()

    for branch in self.branches:

        current = branch.get_contraction()
        if min > current:
            min = current
        if max < current:
            max = current

        total += current

    avg = float(total) / branches_count

    return total, avg, min, max

这两个功能看起来几乎相同,只需稍作修改!

我知道我可以使用sum,min,max等...但是当我将它们应用于我的值时,它们比在循环中执行它们需要更多的时间,因为它们不能立即被调用。

我只想知道为每次计算编写函数是否正确? (也就是专业的方式?)或者我可以编写一个函数并传递列表,对象类型和调用方法。

5 个答案:

答案 0 :(得分:2)

很难说没有看到剩下的代码,但从有限的观点来看,我认为你根本不应该在方法中使用这些函数。我也真的不明白你不使用内置的原因(“它们不能立刻被调用?”)。如果你暗示在python中一次传递中实现4种统计方法比在内置(C)中传递4次更快,那么我担心你的假设非常错误。

那就是说,这是我对这个问题的看法:

def get_stats(l):
    s = sum(l)
    return (
        s,
        float(s) / len(l),
        min(l),
        max(l))

# then create numeric lists from your data and send 'em through:

node_volumes = [n.get_compartment_volume() for n in self.nodes]
branches = [b.get_contraction() for b in self.branches]

# ...

total_1, avg_1, min_1, max_1 = get_stats(node_volumes)
total_2, avg_2, min_2, max_2 = get_stats(branches)

修改

一些基准来证明内置是胜利:

MINE.py

import sys

def get_stats(l):
    s = sum(l)
    return (
        s,
        float(s) / len(l),
        min(l),
        max(l)
    )


branches = [i for i in xrange(10000000)]

print get_stats(branches)

与YOURS.py相对应

import sys

branches = [i for i in xrange(10000000)]

total = 0
min = sys.maxint
max = -1
branches_count = branches.__len__()

for current in branches:
    if min > current:
        min = current
    if max < current:
        max = current

    total += current

avg = float(total) / branches_count

print total, avg, min, max

最后还有一些计时器:

smassey@hacklabs:/tmp $ time python mine.py 
(49999995000000, 4999999.5, 0, 9999999)

real    0m1.225s
user    0m0.996s
sys 0m0.228s
smassey@hacklabs:/tmp $ time python yours.py 
49999995000000 4999999.5 0 9999999

real    0m2.369s
user    0m2.180s
sys 0m0.180s

干杯

答案 1 :(得分:1)

首先,请注意虽然调用len(self.branches)(不要直接调用__len__)可能更有效,但更多 general < / em>在循环中递增计数器,就像使用calculate_volume一样。通过这种更改,您可以按如下方式进行重构:

def _stats(self, iterable, get_current):
    total = 0.0
    min_value = None  # Slightly better
    max_value = -1
    counter = 0
    for n in iterable:
        counter += 1
        current = get_current(n)
        if min_value is None or min_value > current:
            min_value = current
        if max_value < current:
            max_value = current
        total += current
    avg = total / denom
    return total, avg, min_value, max_value

现在,两者中的每一个都可以用_stats

来实现
import operator

def calculate_volume(self):
    return self._stats(self.nodes, operator.methodcaller('get_compartment_volume'))

def get_contraction(self):
    return self.refactor(self.branches, operator.methodcaller('get_contraction'))

methodcaller提供了一个f函数,f('method_name')(x)等同于x.method_name(),它允许您分解方法调用。

答案 2 :(得分:1)

  

或许我可以编写一个函数并传递列表,对象类型和要调用的方法。

尽管你绝对可以将功能传递给功能,但它实际上是一种避免重复自己的常用方法,在这种情况下你不能这样做,因为列表中的每个对象都有它。自己的方法。因此,我将函数的名称作为字符串传递,然后使用getattr以从对象获取实际的可调用方法。另请注意,我使用的是len(),而不是明确调用__len()__

def handle_list(items_list, func_to_call):
    total = 0
    min = sys.maxint
    max = -1
    count = len(items_list)

    for item in items_list:

        current = getattr(item, func_to_call)()
        if min > current:
            min = current
        if max < current:
            max = current

        total += current

    avg = float(total) / count

    return total, avg, min, max

答案 3 :(得分:1)

您可以使用OnNavigationItemSelectedListener.onNavigationItemSelected(MenuItem item)编写一个函数来处理任意对象的列表。

getattr( instance, methodname)

然后在你的课程定义中你需要

def averager( things, methodname):
    count,total,min,max = 0,0,sys.maxint,-1
    for thing in things:
         current = getattr(thing, methodname)()  

         count += 1
         if min > current:
             min = current
         if max < current:
             max = current
         total += current

    avg = float(total) / branches_count
    return total, avg, min, max

答案 4 :(得分:1)

编写一个函数,它接受另一个知道如何从列表中提取值的函数是很常见的。事实上,minmax都会对此类和效果提出论据。

例如

items = [1, 0, -2]
print(max(items, key=abs)) # prints -2

因此编写自己的功能完全可以接受。通常,我只是创建一个新列表,列出您要检查的所有值,然后使用它(例如。[branch.get_contraction() for branch in branches])。但也许空间对你来说是一个问题,所以这里有一个使用生成器的例子。

def sum_avg_min_max(iterable, key=None):
    if key is not None:
        iter_ = (key(item) for item in iterable)
    else:
        # if there is no key, just use the iterable itself
        iter_ = iter(iterable)

    try:
        # We don't know sensible starting values for total, min or max. So use 
        # the first value.
        total = min_ = max_ = next(iter_)
    except StopIteration:
        # can't have a min or max if we have no items in the iterable...
        raise ValueError("empty iterable") from None
    count = 1

    for item in iter_:
        total += item
        min_ = min(min_, item)
        max_ = max(max_, item)
        count += 1

    return total, float(total) / count, min_, max_

然后你可以像这样使用它:

class MyClass(int):
    def square(self):
        return self ** 2

items = [MyClass(i) for i in range(10)]
print(sum_avg_min_max(items, key=MyClass.square)) # prints (285, 28.5, 0, 81)

这是有效的,因为当你从类中获取一个实例方法时,它会自己提供你的底层函数(没有绑定self)。所以我们可以用它作为关键。例如

str.upper("hello world") == "hello world".upper()

使用更具体的示例(假设branches中的项目是Branch的实例):

def get_contraction(self):
    result = sum_avg_min_max(self.branches, key=Branch.get_contraction)
    return result