[Python-ideas] Function composition (was no subject)

Douglas La Rocca larocca at abiresearch.com
Sun May 10 08:06:37 CEST 2015


Thanks! Not sure what took me so long to get on the python lists, but I finally did and to my excitement you were talking about my favorite topic!

---

For replacing the need to write `lambda x: x...` inside compositions *in a limited set of cases*, you could use a sort of "doppelganger" type/metaclass:

class ThisType(type):
    def __getattr__(cls, attr):
        def attribute(*args, **kwargs):
            def method(this):
                this_attr = getattr(this, attr)
                if callable(this_attr):
                    return this_attr(*args, **kwargs)
                else:
                    return this_attr
            return method
        return attribute
    def __call__(cls, *args, **kwargs):
        def decorator(fn):
            return fn(*args, **kwargs)
        return decorator
    def __getitem__(cls, item):
        return lambda x: x[item]

class this(metaclass=ThisType): pass


Basically, it records whatever is done to it, then returns a function that takes a value and does those things to the value. So any call, __getattr__ with arguments, and __getitem__ you'd want to do with a value mid-pipe would be staged or set up by doing them to `this`.

So rather than writing 
>>> compose(lambda s: s.strip('<>'), lambda s: s.lower())('<HTML>')

you can write

>>> compose(this.strip('<>'), this.lower())('<HTML>')
'html'

or

>>> compose(float, this.__str__)('1')
'1.0'

But there are two caveats:

Property attributes would need to be *called*, which feels weird when you already know an API well, so e.g.

>>> from lxml import html
>>> html.fromstring('<b>bold text</b>').text
'bold text'
>>> compose(html.fromstring, this.text())('<b>bold text</b>')
'bold text'

It's also a bit weird because attributes that return functions/methods/callables *aren't* called (like above with `this.__str__`: `__str__` is a method of `float`).

Second caveat is that nothing past the __getitem__ and __getattr__ will work, so e.g.

>>> from pandas import DataFrame
>>> df = DataFrame([1]*2, columns=['A','B'])
   A  B
0  1  1
1  1  1
>>> compose(this.applymap(str), this['A'])(df)
0    1
1    1
Name: A, dtype: object
>>> compose(this.applymap(str), this['A'], this.shape())(df)
(2,)

...but...

>>> compose(this.applymap(str), this['A'].shape)(df)
AttributeError: 'function' object has no attribute 'shape'

________________________________________
From: Python-ideas <python-ideas-bounces+larocca=abiresearch.com at python.org> on behalf of Steven D'Aprano <steve at pearwood.info>
Sent: Sunday, May 10, 2015 2:01 AM
To: python-ideas at python.org
Subject: Re: [Python-ideas] Function composition (was no subject)

On Sun, May 10, 2015 at 04:58:29AM +0000, Douglas La Rocca wrote:
> (Newcomer here.)
>
> I use function composition pretty extensively. I've found it to be
> incredibly powerful, but can lead to bad practices. Certain other
> drawbacks are there as well, like unreadable tracebacks. But in many
> cases there are real benefits. And for data pipelines where you want
> to avoid state and mutation it works well.

Thanks for the well-thought out and very detailed post!

The concrete experience you bring to this discussion is a welcome change
from all the theoretical "wouldn't it be nice (or awful) if ..." from
many of us, and I include myself. The fact that you have extensive
experience with using function composition in practice, and can point
out the benefits and disadvantages, is great.


--
Steve
_______________________________________________
Python-ideas mailing list
Python-ideas at python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


More information about the Python-ideas mailing list