What I'd personally like to see is a combined for-with, something like

    x = [x.split() for x with in open(thisfile)]

and

    for x with in open(somefile):
        print(x)

A with expression might help too,

---
Elazar


2014-02-20 20:50 GMT+02:00 Yann Kaiser <kaiser.yann@gmail.com>:
As an alternative to the recently-proposed "except expression", I
suggested this in its thread. I was recommended to post this
separately, as, while it is related, it is different enough from the
original idea.

The general idea is to extend the context manager protocol so that it
can produce a return value in alternative to simply letting or denying
an exception from propagating, and introduce an inline form of "with".

The motivation behind is, in all honesty, that I find the suggested
except-expressions from PEP 463 to be too verbose to inline, and that
context managers have a much broader reach. The motivation from the
aforementioned PEP(inline evaluation to a "default" expression upon
catching an exception) also applies here.

It could look a bit like this:

    contexted_expr with context_manager as c

Once more, the rationale from PEP 463 also applies here, in that we
can shift "easy defaulting" from being a potential concern for the
callee to being always available to the caller through new syntax.
Likewise, currently in-lining the use of a context manager can be
done, but only through manually encapsulating what would be the
context-ed code through lambdas or eval.

A full example could be as such:

    class Default(object):
        """Context manager that returns a given default value when an exception
        is caught."""

        def __init__(self, value, *exception_types):
            self.value = value
            self.exception_types = exception_types or BaseException

        def __enter__(self):
            pass

        def __exit__(self, typ, val, tb):
            if typ and issubclass(typ, self.exception_types):
                return True, self.value


    lst = [1, 2]
    # found is assigned 2
    found = lst[1] with Default(0, IndexError)
    # not found is assigned 0
    not_found = lst[2] with Default(0, IndexError)

The different interpretation of __exit__'s return value is probably
something that needs to be discussed. In this form, it is potentially
backwards-incompatible, so a preferable alternative would be a
different special method, perhaps:

    def __return__(self, typ, val, tb, ret):
        if typ is None:
            return False, ret * 3
        elif isinstance(typ, IndexError):
            return True, 10

The alternatively-named special method would take priority over
__exit__ and take over its augmented function:
If no exception has occurred, typ, val, and tb are given None, as with
the regular __exit__ incarnation, but ret, or whatever is the fourth
positional parameter, is supplied with what "expr" evaluated to in
"expr with cmgr". When an exception has occurred or propagated, typ,
val and tb are set to the appropriate values(Since exceptions now keep
their traceback as an attribute, maybe only supply the exception
object?), and ret is given None.
If the return value of this special method is None, the exception or
return value is propagated as is. If it is a sequence, the first
element is used like the return value of __exit__ would be, and the
second element is used to (re-)place the return value of the whole
with expression. When multiple context managers are being chained, the
return value/exception is forwarded much like it is with twisted's
Deferreds.

In the use case of providing a default value, if the default value is
the product of an expensive operation, an alternative context manager
can be designed to compute the value only when needed, for instance:

    fa = factorials[n] with SetDefault(factorials, n, lambda: math.factorial(n))

Other examples using existing context managers:

    contents = f.read() with open('file') as f

    with open('file') as f:
        contents = f.read()



    d = Decimal(1) / Decimal(7) with Context(prec=5)

    with Context(prec=5):
        d = Decimal(1) / Decimal(7)

I think that's all I can think of so far. Sorry as this might be a
little too detailed to start with, so I will remind you there is no
offense in rethinking any of what I posted here.

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