Using Python 3.8 assignment expressions as a let expression?

732 Views Asked by At

My intended behavior is:

>>> x = 0
>>> with (x := 1): print(x)
1
>>> print(x)
0

However I get the expected AttributeError: __enter__ error. Is there an easy way to achieve this, or something similar that lets me compensate for not having Lisp-style let expression?

P.S. I know I can do something like:

class Let(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
    def __enter__(self):
        return self
    def __exit__(self, *args):
        return None

And then:

>>> with Let(x=1) as let: print(let.x)

Or:

>>> with (let := Let(x=1)): print(let.x)

But having to say let.x as opposed to just x makes it too ugly!

3

There are 3 best solutions below

1
On

:= operation isn't like let in JavaScript, it's a operation to assign a value to a variable and output the value at the same time. Loosely speaking, let is declare a local variable ie. you can't use this variable outside the scope.

I'm not sure what you mean by 'let expression', but := is different from scope differentiated assignment.

0
On

Using your existing Let class, you could change the string representation of the class using __str__ (as per this answer). Then just call your instance ‘x’ and print(x) will work

2
On

I don't know why I didn't find this before posting this question (https://nvbn.github.io/2014/09/25/let-statement-in-python/)

It behaves just like I want:

from contextlib import contextmanager
from inspect import currentframe, getouterframes

@contextmanager
def let(**bindings):
    frame = getouterframes(currentframe(), 2)[-1][0]
    locals_ = frame.f_locals
    original = {var: locals_.get(var) for var in bindings.keys()}
    locals_.update(bindings)
    yield
    locals_.update(original)


x = 0

with let(x=1):
    print(x)

print(x)

It needs tweaking a bit, though. For example undefined variables return None within let, as opposed to a NameError.

I still don't know how to do recursive lets without wrapping it in a function. I.e. something like:

>>> with let(x=1, y=x+1):
       print(x, y)
1 2

Inputs are appreciated!

Update

This answer won't work properly, because you can't update locals()... well, back to square one.