On 27 March 2018 at 01:11, Guido van Rossum <[email protected]> wrote:
> Honestly that sounds way too complex. In my ideal world, only functions
> defined with 'def' are methods whose __get__ auto-binds the first argument
> (unless @classmethod or @staticmethod is present), and do not get to "see"
> the class scope. This is how you define methods after all. Lambdas used at
> class scope do not get this benefit, and they see the class scope as just
> another closure; ditto for comprehensions and genexprs.
>
> I don't think that the "is the call delayed" idea is the proper way to
> distinguish the two cases here. IMO the key concept is "is it used to define
> a method". I betcha if you see a lambda here it's because there's some
> calculation going on whose result will be stored as a class variable.

Aye, I don't disagree with that. The implicit functions used in the
comprehension & generator expression cases are just potentially
simpler to handle, as we don't care about their API signatures, which
means we can freely pollute their APIs with eager name bindings if we
choose to do so.

Lambda expressions are different, since their public API matters, so
we can't mess with it freely to pass in extra name references.

What we potentially *could* do though is allow implicit additions to
their __closure__ attributes in a way that isn't permitted for regular
function definitions, such that in the following code:

    class C:
        x = 1
        f = staticmethod(lambda: print(x))

we would do the following things differently for lambdas (vs def functions):

1. We'd pass "x" down as a candidate for nonlocal name resolution
while compiling the lambda
2. If "x" did get referenced from a lambda, we'd start allowing
non-optimised code objects to have a list of cell vars (the way
functions already do), and define a new "STORE_CLASSDEREF" opcode (as
the counterpart to LOAD_CLASSDEREF)

What STORE_CLASSDEREF would need to do is write updates both to the
current locals namespace (so the expectations of a class body are met)
*and* to the nominated cell object (so any references from lambda
expressions are updated).

The biggest downside I'd see to this is that for an incredibly long
time, we've pushed the view that "lambda expressions are just regular
functions that don't know their own name". Making it so that lambdas
can close over class attributes breaks that equivalence, and if we
were to use the cell based approach I suggest above, the seams would
be visible in the case where a lambda expression references an
attribute that gets rebound after class creation:

    >>> C.f()
    1
    >>> C.x = 2
    >>> C.f() # With a cell based approach, this wouldn't change
    1

All of those potential complexities are specific to lambda functions
though - since the implicit functions created for lambda expressions
and generator expressions are called and then immediately thrown away,
they don't need a persistent cell reference to the closed over
variable name.

Cheers,
Nick.

-- 
Nick Coghlan   |   [email protected]   |   Brisbane, Australia
_______________________________________________
Python-ideas mailing list
[email protected]
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to