说我有这个简单的课程:
class Foo(object):
def __init__(self, number, name):
self.number = number
self.name = name
和Foo实例列表:
l = [Foo(10, 'a'), Foo(9, 'a'), Foo(8, 'a'), Foo(7,'a'), Foo (5, 'b'), Foo (4, 'b') ,Foo (3, 'b')]
假设'name'属性只能是'a'或'b'。
以最快的方式提取“名称”为“a”(或“b”)的所有对象的子列表是什么?请注意,此操作可能会被调用数百万次,这就是为什么我要尽可能地优化它。
注意列表的构建方式使得所有元素在列表的第一个或第二个半部分中“组合在一起”。该列表是对称的,并按递减属性'number'排序。 编辑:不一定有相同数量的'a'和'b'。
我是怎么做到的:
一开始我只是做了一个for循环:
sublist = []
for o in l:
if o.name == 'a'
sublist.append(o)
然后我尝试了列表理解:
sublist = [o for o in l if o.name=='a']
但是,如果不是有点慢,这似乎差不多。
无论哪种方式,这些都没有利用所有属性已经在原始(排序)列表中“组合在一起”的假设。即使不再需要它也会保持循环。速度非常重要,所以我需要它尽可能高效。
答案 0 :(得分:2)
在匹配
后遇到不匹配时,只需退出循环sublist = []
for o in l:
if o.name == 'a'
sublist.append(o)
elif sublist:
break
如果您想使用生成器,可以使用itertools
函数
from itertools import takewhile, dropwhile
sublist = list(takewhile(lambda o: o.name == 'a', dropwhile(lambda o: o.name != 'a', l))
这两个都利用列表已排序的事实,并在项目停止匹配后停止处理列表。
答案 1 :(得分:2)
由于name
属性只能是'a'或'b',它们是有序的,并且你有相同数量的'a'和'b',最简单的方法是找到中间点和切片列表:
mid = int(len(aList)/2)
sublist = l[:mid]
以上将为您提供所有'a'而l[mid:]
给出所有'b'。
修改:由于问题已经改变,'a'和'b'的元素数量相同,上述答案不再适用。
根据列表的长度,我的猜测是二元搜索(对于更长的列表)或者像Brendan建议的那样(对于较短的列表)突破循环将是最快的方法。
答案 2 :(得分:2)
使用二进制搜索在O(logN)中找到中间点:
In [19]: class Foo(object):
...: def __init__(self, number, name):
...: self.number = number
...: self.name = name
...:
...: def __repr__(self):
...: return 'Foo(number={self.number}, name={self.name})'.format(self=self)
...:
In [20]: def binary_search(lst, predicate):
...: """
...: Finds the first element for which predicate(x) == True
...: """
...: lo, hi = 0, len(lst)
...: while lo < hi:
...: mid = (lo + hi) // 2
...: if predicate(lst[mid]):
...: hi = mid
...: else:
...: lo = mid + 1
...: return lo
...:
In [21]: l = [Foo(10, 'a'), Foo(9, 'a'), Foo(8, 'a'), Foo(7,'a'), Foo (5, 'b'), Foo (4, 'b'
...: ) ,Foo (3, 'b')]
In [22]: binary_search(l, lambda x: x.name == 'b')
Out[22]: 4
In [23]: l[:binary_search(l, lambda x: x.name == 'b')]
Out[23]:
[Foo(number=10, name=a),
Foo(number=9, name=a),
Foo(number=8, name=a),
Foo(number=7, name=a)]
In [24]: l[binary_search(l, lambda x: x.name == 'b'):]
Out[24]: [Foo(number=5, name=b), Foo(number=4, name=b), Foo(number=3, name=b)]
然而,请注意: