Skip to content

Instantly share code, notes, and snippets.

@louisswarren
Last active July 6, 2020 03:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save louisswarren/31cc05630d9af5b582d17f294abdc414 to your computer and use it in GitHub Desktop.
Save louisswarren/31cc05630d9af5b582d17f294abdc414 to your computer and use it in GitHub Desktop.
Python proposals

Python Proposals

cast decorator

The common python pattern

def foo():
    tmp = []
    for x in y:
        ...
        tmp.append(z)
    return tmp

can be simplified to

@cast
def foo() -> list:
    for x in y:
        ...
        yield z

using the decorator cast, which I have implemented here.

Some examples:

@cast
def nums() -> dict:
    yield 1, 'one'
    yield 2, 'two'
# {1: 'one', 2: 'two'}

@cast
def hello() -> (str.upper, ', '.join):
    yield "Hello"
    yield "world"
# 'HELLO, WORLD'

A decorator for function composition as follows would be useful.

compose = lambda f: lambda g: lambda *a, **k: f(g(*a, **k))

@compose(str.upper)
@compose(', '.join)
def hello():
    yield "Hello"
    yield "world"

However, the notation achieved by cast is superiour.

continue for coroutines

We can nicely loop through generators using for loops. However, for coroutines, this is not possible, since we have no way of sending values. I therefore suggest allowing continue to take an optional expression to send to the generator. For example, in

for x in g:
    ...
    continue y

the first value of x would be obtained from next(g) as usual, but subsequent values are obtained from g.send(y) (of course, allowing continue to be used in branches as it is currently, with g.send(None) or next(g) used if an iteration ends without a continue).

Currently this has to be done using the more clumsy

x = next(g)
try:
    while True:
        ...
        x = g.send(y)
except StopIteration:
    pass

finally for getting the StopIteration value from a for loop

Iterators raise StopIteration exceptions to stop. In particular, generators raise the exception when they return, with the value property set to the returned value. I suggest using the finally keyword for accessing this as follows:

def gen():
    yield 'foo'
    return 'bar'

for x in gen():
    print(x)
finally y:
    print(y) # prints 'bar'

Supplying a variable to finally would be optional.

This would also be useful with the else case for for loops: the else case happens if the loop ends without breaking, and the finally case occurs regardless. Placing the finally case before the else case would simplify some code.

If the loop is terminated by break, the value should be None. Moreover, break could be extended to also take an expression to give to finally.

for x in range(1):
    break 'bar'
finally y:
    print(y) # prints 'bar'

else assertion

else EXPRESSION:
    ...

as shorthand for

else:
    assert(EXPRESSION)
    ...

though perhaps a new exception type should be used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment