2012-11-29 73 views
1

我有一些舊代碼,我在Python中將函數列表存儲爲類屬性。這些列表被用作一種事件掛鉤。Python單線程調用函數列表

要用適當的參數調用列表中的每個函數,我已經使用了單行,將maplambda表達式混合使用。我現在擔心在使用lambda這樣的表達式時會產生不必要的開銷。我推薦使用maplambda這兩個表達式,並且只是使用循環標準來提高可讀性。

雖然有更好的(讀得更快)單線程來做到這一點,但?

例如:

class Foo: 
    """Dummy class demonstrating event hook usage.""" 
    pre = [] # list of functions to call before entering loop. 
    mid = [] # list of functions to call inside loop, with value 
    post = [] # list of functions to call after loop. 
    def __init__(self, verbose=False, send=True): 
     """Attach functions when initialising class.""" 
     self._results = [] 
     if verbose: 
      self.mid.append(self._print) 
     self.mid.append(self._store) 
     if send: 
      self.post.append(self._send) 

    def __call__(self, values): 

     # call each function in self.pre (no functions there) 
     map(lambda fn: fn(), self.pre) 

     for val in values: 
      # call each function in self.mid, with one passed argument 
      map(lambda fn: fn(val), self.mid) 

     # call each fn in self.post, with no arguments 
     map(lambda fn: fn(), self.post) 

    def _print(self, value): 
     """Print argument, when verbose=True.""" 
     print value 

    def _store(self, value): 
     """Store results""" 
     self._results.append(value) 

    def _send(self): 
     """Send results somewhere""" 

# create instance of Foo 
foo = Foo(verbose=True) 

# equivalent to: foo.__call__(...) 
foo([1, 2, 3, 4]) 

有沒有更好的方式來寫這些的單行map電話?

回答

2

推薦的方法是絕對然而使用for循環,如果你堅持要用map,然後operator.methodcaller可能是你需要的東西:

>>> def foo(*args): 
... print 'foo',args 
... 
>>> def bar(*args): 
... print 'bar',args 
... 
>>> from operator import methodcaller 
>>> 
>>> map(methodcaller('__call__',1,2,3),[foo,bar]) 
foo (1, 2, 3) 
bar (1, 2, 3) 
[None, None] 

謹慎的關於使用map這一個字 - 如果您將代碼移植到python 3,它將不起作用,因爲map變得很懶。

你也可以使用列表理解很平凡(這也適用於python3):

[fn() for fn in self.pre] 
[fn(val) for fn in self.mid] 

+0

'operator.methodcaller'對我來說是一個新的:)但我不確定我會使用它,爲了清晰起見。感謝關於Python3中的map的警告。 –

1

的所有「我擔心有不必要的開銷,」首先是沒有優化代碼的方法。使用分析器來查找熱點。其次,你的代碼可以做評論,讓讀者知道發生了什麼。

最後,直到證明並非如此,下面是一個很好的方式來完成任務:

for func in self.pre: func() 
#apply every function in self.mid to every value in values 
for func,val in itertools.product(self.mid, values): 
    func(val) 

如果你想捕捉的值,你可以使用一個列表理解;如果你想延遲評估,你可以使用生成器表達式。

+0

+1(爲了不構建不必要的返回值列表)。但我想你想用'itertools.product'而不是'zip'? – moooeeeep

+0

@moooeeeep我明白了。也就是說,我將OP引用到我的第二點:他的代碼需要評論。 – Marcin

+0

我以前見過lambda表達式出現在cProfile結果中,有時相當顯着,這是由於調用的次數。所以我問。我相信你們是對的。 for循環顯然是最好的選擇! –