[Python-Dev] functools.compose to chain functions together

Scott Dial scott+python-dev at scottdial.com
Mon Aug 17 09:50:04 CEST 2009


Greg Ewing wrote:
> Jason R. Coombs wrote:
>> I had a use case that was compelling enough that I thought there
>> should be something in functools to do what I wanted.
> 
> I think this is one of those things that a small minority of
> people would use frequently, but everyone else would use
> very rarely or never. The decision on whether to include
> something in the stdlib needs to be based on the wider
> picture.
> 
> In this case, it's trivial to write your own if you want
> it. As they say, "not every one-line function needs to
> be in the stdlib".
> 

I have never found these arguments compelling. They are obviously not
true (e.g., itertools.compress()[1] added in 2.7/3.1), and so what I
really hear is: "I don't like it and I outrank you."

I can't help invoke part of PEP309's justification for
functools.partial()[2]:

"""
I agree that lambda is usually good enough, just not always. And I want
the possibility of useful introspection and subclassing.
"""

The same reasoning would seem to apply here. In the OP's example, the
meta-decorator becomes opaque due to the use of a lambda. If one could
introspect a compose(), then introspection tools could actually know the
set of decorators being applied. As it is, the "preferred" method of
using a lambda actually makes it quite hard to know anything.

class compose():
    def __init__(self, *funcs):
        if not funcs:
            raise ValueError(funcs)
        self.funcs = funcs

    def __call__(self, *args, **kwargs):
        v = self.funcs[-1](*args, **kwargs)
        for func in reversed(self.funcs[:-1]):
            v = func(v)
        return v

meta = functools.compose(decorator_a, decorator_b)
print meta.funcs

meta = lambda f: decorator_a(decorator_b(f))
# impossible, short of disassembling the lambda

-Scott

[1] http://docs.python.org/3.1/library/itertools.html#itertools.compress

"""
def compress(data, selectors):
    # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
    return (d for d, s in zip(data, selectors) if s)
"""


More information about the Python-Dev mailing list