Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there something like the threading macro from Clojure in Python?

In Clojure I can do something like this:

(-> path
      clojure.java.io/resource
      slurp
      read-string)

instead of doing this:

(read-string (slurp (clojure.java.io/resource path)))

This is called threading in Clojure terminology and helps getting rid of a lot of parentheses.

In Python if I try to use functional constructs like map, any, or filter I have to nest them to each other. Is there a construct in Python with which I can do something similar to threading (or piping) in Clojure?

I'm not looking for a fully featured version since there are no macros in Python, I just want to do away with a lot of parentheses when I'm doing functional programming in Python.

Edit: I ended up using toolz which supports pipeing.

like image 256
Hexworks Avatar asked May 04 '18 13:05

Hexworks


People also ask

What is arrow in Clojure?

Threading macros, also known as arrow macros, convert nested function calls into a linear flow of function calls, improving readability.

What is a macro in Clojure?

Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.


2 Answers

Here is a simple implementation of @deceze's idea (although, as @Carcigenicate points out, it is at best a partial solution):

import functools
def apply(x,f): return f(x)
def thread(*args):
    return functools.reduce(apply,args)

For example:

def f(x): return 2*x+1
def g(x): return x**2
thread(5,f,g) #evaluates to 121
like image 134
John Coleman Avatar answered Nov 14 '22 21:11

John Coleman


I wanted to take this to the extreme and do it all dynamically.

Basically, the below Chain class lets you chain functions together similar to Clojure's -> and ->> macros. It supports both threading into the first and last arguments.

Functions are resolved in this order:

  1. Object method
  2. Local defined variable
  3. Built-in variable

The code:

class Chain(object):
    def __init__(self, value, index=0):
        self.value = value
        self.index = index
    def __getattr__(self, item):
        append_arg = True
        try:
            prop = getattr(self.value, item)
            append_arg = False
        except AttributeError:
            try:
                prop = locals()[item]
            except KeyError:
                prop = getattr(__builtins__, item)
        if callable(prop):
            def fn(*args, **kwargs):
                orig = list(args)
                if append_arg:
                    if self.index == -1:
                        orig.append(self.value)
                    else:
                        orig.insert(self.index, self.value)
                return Chain(prop(*orig, **kwargs), index=self.index)
            return fn
        else:
            return Chain(prop, index=self.index)

Thread each result as first arg

file = Chain(__file__).open('r').readlines().value

Thread each result as last arg

result = Chain(range(0, 100), index=-1).map(lambda x: x * x).reduce(lambda x, y: x + y).value
like image 20
Brian Baritonehands Gregg Avatar answered Nov 14 '22 22:11

Brian Baritonehands Gregg