我对生物数据进行了一些计算。每个函数计算一个对象列表的总值,平均值,最小值,最大值。 我的想法是,我有很多不同的列表,每个列表都是针对不同的对象类型。 我不想为每个函数重复我的代码只是更改“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等...但是当我将它们应用于我的值时,它们比在循环中执行它们需要更多的时间,因为它们不能立即被调用。
我只想知道为每次计算编写函数是否正确? (也就是专业的方式?)或者我可以编写一个函数并传递列表,对象类型和调用方法。
答案 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)
编写一个函数,它接受另一个知道如何从列表中提取值的函数是很常见的。事实上,min
和max
都会对此类和效果提出论据。
例如
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