2012-08-29 174 views
18

链接(不要与itertools.chain混淆)方法蟒蛇

我读了以下内容: http://en.wikipedia.org/wiki/Method_chaining

我的问题是:什么是在Python中实现方法链的最佳方式?

这里是我的尝试:

class chain(): 
    def __init__(self, my_object): 
     self.o = my_object 

    def __getattr__(self, attr): 
     x = getattr(self.o, attr) 
     if hasattr(x, '__call__'): 
      method = x 
      return lambda *args: self if method(*args) is None else method(*args) 
     else: 
      prop = x 
      return prop 

list_ = chain([1, 2, 3, 0]) 
print list_.extend([9, 5]).sort().reverse() 

""" 
C:\Python27\python.exe C:/Users/Robert/PycharmProjects/contests/sof.py 
[9, 5, 3, 2, 1, 0] 
""" 

的一个问题是,如果在调用method(*args)修改self.o但不返回None。 (那么我应该返回self还是返回method(*args)返回)。

有没有人有更好的方法来实现链接?可能有很多方法可以做到这一点。

我应该假设一个方法总是返回None,所以我总是可以返回self.o

+0

(注意,我不确定是否应该在Python中使用方法链接,但我仍然感兴趣) –

+0

您应该使用[纯函数](http://en.wikipedia.org/wiki/Pure_function),以便方法不直接修改'self.o',而是返回修改后的版本。另外'__getattr__'应该返回一个链式实例。 – reubano

回答

16

有一个非常有趣的​​库可能是你的问题的答案。例如::

seq = fib() | take_while(lambda x: x < 1000000) \ 
      | where(lambda x: x % 2) \ 
      | select(lambda x: x * x) \ 
      | sum() 
+2

+1习惯了C#的LINQ之后,'Pipe'是我在Python中导入的第一个东西。 (虽然你需要注意'from pipe import *') – Kos

+4

无论如何,'Pipe'用于组合函数 - 将函数A的结果作为函数B的参数传递。链接(通常)是关于从为了在一个链中进行多次修改而进行了几次调用。 JQuery已经普及了很多。 – Kos

+1

是的。但我相信链接不应该像在JavaScript中一样用在Python中。 –

4

没有将要被链接允许任何对象的任何方法,任何普通的方式,因为你无法知道什么样的值方法返回,为什么不知道具体的方法是如何工作的。出于任何原因,方法可能会返回None;它并不总是意味着该方法已经修改了该对象。同样,返回值的方法仍然可能不会返回可链接的值。有没有办法来链状list.index的方法:​​不能有工作的希望,因为index整点时,它返回一个数字,这个数字意味着什么,而不能忽视的只是链条上的原目的。

如果你只是摆弄Python的内建类型来链接某些特定的方法(如排序和移除),那么最好是明确地包装这些特定的方法(通过在你的包装类中重写它们),而不是试图用__getattr__做一个通用机制。

+0

谢谢 - 你说得对。我不认为这有可能是一般。我能想到的最好方法就是让程序员决定是否要连锁。例如。默认情况下,它不返回self(它只是返回方法返回的内容),但是如果程序员显式地去了list_.chain.sort(),它可能会返回list_而不是None。 –

+3

@robertking:对,一般的做法是,方法链不是您可以轻松添加到现有对象的东西。你基本上必须设计一个方法链接的方法,以使其正常工作。 – BrenBarn

13

这是可能的,如果你只使用pure functions,这样的方法没有修改self.data直接,而是返回修改后的版本。您还必须返回Chainable实例。

下面是使用collection pipelining用列表的一个例子:

import itertools 

try: 
    import builtins 
except ImportError: 
    import __builtin__ as builtins 


class Chainable(object): 
    def __init__(self, data, method=None): 
     self.data = data 
     self.method = method 

    def __getattr__(self, name): 
     try: 
      method = getattr(self.data, name) 
     except AttributeError: 
      try: 
       method = getattr(builtins, name) 
      except AttributeError: 
       method = getattr(itertools, name) 

     return Chainable(self.data, method) 

    def __call__(self, *args, **kwargs): 
     try: 
      return Chainable(list(self.method(self.data, *args, **kwargs))) 
     except TypeError: 
      return Chainable(list(self.method(args[0], self.data, **kwargs))) 

使用方法如下:

chainable_list = Chainable([3, 1, 2, 0]) 
(chainable_list 
    .chain([11,8,6,7,9,4,5]) 
    .sorted() 
    .reversed() 
    .ifilter(lambda x: x%2) 
    .islice(3) 
    .data) 
>> [11, 9, 7] 

注意.chainitertools.chain而不是OP的chain