(I started writing this this morning, and there’s been quite a bit more discussion since, but I don’t think it’s completely too late)
Thanks Paul (and others that have commented). All reasonable considerations, and of course what the SC will be thinking about. However, a key question.
This is what confuses me. Yes, the evaluation of late-bound defaults is technically “deferred” until the function is called, but it is a very specific use case, with the time and namespace(s) of evaluation clearly defined. I presume a “general deferred expression” object would provide some flexibility on both those counts, or it wouldn’t be a general solution at all. Sure, a general purpose deferred expression could be used to simulate late-bound defaults, but you would still need to write extra code or have special syntax or meaning for late bound defaults. Or have them be poorly defined.
Could someone making this argument please propose a possible deferred expression symtax, and how it would be used for late-bound defaults?
Otherwise, it seems completely irrelevant to the topic at hand.
Example:
Let’s say that we have:
x = deferred exp
Where deferred is a keyword, and exp is an expression which is not evaluated right away.
So: when does exp get evaluated? Maybe when you reference x?
OK, now:
y = x
Will result in exp being evaluated.
But what namespace does it use? The one where it’s evaluated? Or the one where it was defined? Pick one.
OK, now we have:
def fun(x = deferred exp):
Now when does expr get evaluated? If it’s when it’s used, then either the result depends on what’s changed in the namespace where the function is defined, or the result depends on what’s changed it the function’s namespace.
So:
def fun(n, x = deferred n**2):
y = x
Gives a different value for y than
def fun(n, x = deferred n**2):
n += 2
y = x
Or n is looked up
In the global namespace, and that could lead to some real WTF moments.
Which makes defining the default in the signature pretty pointless— reading the signature wouldn’t give you any new info.
So then we’re back to saying that a deferred expression always gets evaluated in the function namespace, and at the beginning of the function. Which is, well, pretty much what the PEP is proposing.
Maybe a deferred expression gets evaluated when specifically asked to:
y = realize x
OK, but then when you look at a function signature, you’ll have no idea when the exp will be realized — so no idea what it will do. So, back to having to explain it in the docstring, like we do for the sentinel approach.
I’m sure you all think I’ve made a straw man here — something poorly designed so I can shoot it down, but I’ve honestly done my best.
If some one can come up with a general deferred expression approach that can also be used clearly and simply as a late bound default—please let us know!
-CHB
-- Christopher Barker, PhD (Chris)
Python Language Consulting
- Teaching
- Scientific Software Development
- Desktop GUI and Web Development
- wxPython, numpy, scipy, Cython