Packaged
-> hachibeeDI / letexpr
It is a story
State monad in Python I was writing this article and wanted a let expression like Haskell in Python, so I thought about it.
Then, in the related post Make let's one-line programming Because there was something like that, I will make it feel good to divert it. I thought, but I gave up diversion because it looks like apply. So I will do my best.
The let expression seems to bind inside the let and evaluate the expression with in. Also, it seems that variables bound in let can be referenced from the entire let expression.
For the time being, let's bind the result to something variable so that it can be used in
class let(object):
def __init__(self, action=None):
if action is None:
name, act = action
self.lets = {name: act()}
else:
self.lets = {}
def __or__(self, action):
''' :type action: (str, func) '''
name, f = action
self.lets[name] = f()
return self
def in_(self, func):
return func(**self.lets)
if __name__ == '__main__':
x = let(('xx', lambda : 1 + 2)) | ('y', lambda : 'yyy') | ('z', lambda : 5)
print x.in_(lambda xx, y, z: str(xx) + y + str(z))
It became a form for the time being. However, as it is, it is not possible to refer to the value between let expressions, so I will make it feel good.
As mentioned above, in Python you can pass a value to the argument corresponding to the key by passing a ** dictionary
to the argument of the function, but unlike some popular languages, the argument An error will be thrown for excess or deficiency of.
In other words, you only need to pass the values that each needs in the results stored in self.lets_
.
In such a case, the ʻinspect module is convenient, so we use the
getargspec` function to pass only the necessary arguments.
from inspect import getargspec
class let(object):
def __init__(self, action=None):
if action is not None:
name, act = action
self.lets = {name: act()}
else:
self.lets = {}
def __or__(self, action):
''' :type action: (str, func) '''
name, f = action
require_arg_keys = getargspec(f).args
self.lets[name] = f(
**self.__extract_require_args(require_arg_keys)
)
return self
def __extract_require_args(self, arg_keys):
return {k: v for k, v in self.lets.iteritems() if k in arg_keys}
def in_(self, func):
require_arg_keys = getargspec(func).args
return func(
**self.__extract_require_args(require_arg_keys)
)
Now, let's try by passing an expression that actually requires various arguments. Let's make the indentation cool.
measure = \
(let()
| ('x', lambda : 10)
| ('y', lambda : 20)
| ('size', lambda x, y: x * y)
| ('hoge', lambda x, y: 'fooo')
) \
.in_(lambda x, y, size:
'x = {x}, y = {y}, x * y = {size}'.format(x=x, y=y, size=size))
print measure # => x = 10, y = 20, x * y = 200
You did it, did not you. In the current situation, we are evaluating from top to bottom, so we can not get the upper value from the lambda below, but if we do our best to delay the evaluation of the expression, we will be able to do that as well. I agree. I thought that normal is the best because the one who wrote it normally can read it normally.
Recommended Posts