
On Tue, Jan 19, 2016 at 4:37 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, Jan 19, 2016 at 08:47:28AM -0800, Guido van Rossum wrote:
I think it's reasonable to divert this discussion to "value capture". Not sure if that's the usual terminology, but the idea should be that a reference to the value is captured, rather than (as Python normally does with closures) a reference to the variable (implemented as something called a "cell").
If I understand you correctly, that's precisely what a function default argument does: capture the current value of the default value expression at the time the function is called.
I think you misspoke here (I don't think you actually believe what you said :-). Function defaults capture the current value at the time the function is *define*.
This has the side-effect of exposing that as an argument, which may be underdesirable.
Indeed. It's also non-obvious to people who haven't seen it before.
partial() can be used to work around that.
Hardly. Adding a partial() call usually makes code *less* obvious.
The best syntax for such capture remains to be seen. ("Capture" seems to universally make people think of "variable capture" which is the opposite of what we want here.)
If I recall correctly, there was a recent(?) proposal for a "static" keyword with similar semantics:
def func(a): static b = expression ...
would guarantee that expression was evaluated exactly once.
Once per what? In the lifetime of the universe? Per CPython process start? Per call? J/K, I think I know what you meant -- once per function definition (same as default values).
If that evaluation occurred when func was defined, rather than when it was first called,
(FWIW, "when it was first called" would be a recipe for disaster and irreproducible results.)
that might be the semantics you are looking for:
def func(a): static b = b # captures the value of b from the enclosing scope
Yeah, I think the OP proposed 'capture b' with these semantics.
Scoping rules might be tricky to get right. Perhaps rather than a declaration, "static" might be better treated as a block:
Why? This does smell like a directive similar to global and nonlocal.
def func(a): static: # Function initialisation section. Occurs once, when the # def statement runs. b = b # b on the left is local, b on the right is non-local # (just like in a parameter list)
Hm, this repetition of the name in parameter lists is actually a strike against it, and the flexibility it adds (of allowing arbitrary expressions to be captured) doesn't seem to be needed much in reality -- the examples for the argument default pattern invariably use 'foo=foo, bar=bar'.
# Normal function body goes here.
But neither of these approaches would be good for lambdas. I'm okay with that -- lambda is a lightweight syntax, for lightweight needs. If your needs are great (doc strings, annotations, multiple statements) don't use lambda.
Yeah, the connection with lambdas in C++ is unfortunate. In C++, IIRC, the term lambda is used to refer to any function nested inside another, and that's the only place where closures exist. -- --Guido van Rossum (python.org/~guido)