2013-02-11 31 views
0

基本上我想要做的是取兩個對象列表並根據一些測試將它們分成兩個列表,取決於結果是True還是Falsefilter()的作用是排序,除了不是確定它是否處於進入或退出狀態,而是確定要去哪個列表/迭代器。我做了使用itertools.groupby()這樣的嘗試:pythonic方式正好分爲兩個組的列表

import random 
from itertools import groupby 

class FooObject(object): 
    def __init__(self): 
     self.key = random.choice((True, False)) 

    def __repr__(self): 
     return '<Foo: %s>' % self.key 

foos = [FooObject() for _ in range(10)] 
left, right = [], [] 

groups = groupby(sorted(foos, key=lambda o: o.key), lambda o: o.key) 
for k, group in groups: 
    if k: 
     right = list(group) 
    else: 
     left = list(group) 

print left 
print right 

這幹得不錯,只是不知道是否有一個推行清潔/簡單的方法。我意識到我可以使用filter()(或等效的列表理解),並通過兩遍,但有什麼樂趣呢?

回答

1

如果你只有2桶,你可以使用一個三元:

d={'left':[],'right':[]} 
for e in (random.random() for i in xrange(50)): 
    d['left' if e<0.5 else 'right'].append(e) 

使用超過2個存儲桶時,使用返回已定義的密鑰或使用默認字典與列表的功能:

def f(i): 
    return int(i*10) 

DoL=defaultdict(list) 
for e in (random.random() for i in xrange(50)): 
    DoL[f(e)].append(e) 
1

下面是消耗源只有一次,並返回一個類似於字典的對象的功能,其中每個部分是一個發生器,作爲懶惰地從源得到的值作爲可能的:使用的

def partition(it, fun): 

    class x(object): 
     def __init__(self): 
      self.buf = {} 

     def flush(self, val): 
      for p in self.buf.get(val, []): 
       yield p 
      self.buf.pop(val, None) 

     def __getitem__(self, val): 
      for p in self.flush(val): yield p 
      while True: 
       try: 
        p = next(it) 
       except StopIteration: 
        break 
       v = fun(p) 
       if v == val: 
        yield p 
       else: 
        self.buf.setdefault(v, []).append(p) 
      for p in self.flush(val): yield p 

    return x() 

實施例:

def primes(): # note that this is an endless generator 
    yield 2 
    p, n = [], 3 
    while True: 
     if all(n % x for x in p): 
      p.append(n) 
      yield n 
     n += 2 


p = partition(primes(), lambda x: x % 3) 
# each member of p is endless as well 

for x in p[1]: 
    print x 
    if x > 200: break 

for x in p[2]: 
    print x 
    if x > 200: break 
相關問題