On Fri, Dec 3, 2021 at 12:48 PM Steven D'Aprano <st...@pearwood.info> wrote: > > On Fri, Dec 03, 2021 at 02:10:12AM +1100, Chris Angelico wrote: > > > > > Unfortunately not, since the default expression could refer to other > > > > parameters, or closure variables, or anything else from the context of > > > > the called function. So you won't be able to externally evaluate it. > > > > > > Why not? Functions can do all those things: refer to other variables, or > > > closures, or anything else. You can call functions. Are you sure that > > > this limitation of the default expression is not just a limitation of > > > your implementation? > > > > def f(): > > a = 1 > > def f(b, c=>a+b): return c > > a = 2 > > return f > > > > If there were a function to represent the late-bound default value for > > c, what parameters should it accept? > > I'm not saying that it *must* be a function. It could be a bare code > object, that is `eval()`ed. Or something completely new. Dunno. > > But you're saying something is impossible, and that seems implausible to > me, because things that seems *very similar* are totally possible. > > > > How would you externally evaluate this? > > > inner = f() # Get the inner function. > default = inner.__code__.__late_defaults__.wibble[1] # whatever > try: > value = default() > except NameError: > # Well, what did you expect to happen? > value = eval(default.__code__, globals(), {'a': 101, 'b': 202}) > > > Or something. The point is, rather than dismissing the possibility > outright, this should be something we discuss, and carefully consider, > before the PEP is complete.
How, with external calling, are you going to know which name references to look up, and where to get their values from? This is already a virtually impossible task in Python, which is why f-strings cannot be implemented as str.format(**something) for any known meaning of "something". You cannot get your *current* variable set, much less the available variables in some other context. > > And it is potentially a LOT of unnecessary overhead. Consider this edge > > case: > > > > def f(a, b=>c:=len(a)): ... > > > > In what context should the name c be bound? > > With late-bound defaults, the expression is evaluated at function call > time, in the scope of f()'s locals. So c would be a local. In other words, if you're trying to evaluate b's default externally, you have to set c in a context that doesn't even exist yet. Is that correct? > The third obvious answer is that if either the decision or the > implementation is really too hard, then make it a syntax error for now, > and revisit it in the future. Everything's perfectly well defined, save that you can't externally evaluate the defaults. You can quite happily use the walrus in a late-bound default, and it'll bind to the function's locals. (Though I don't recommend it. I think that that's going to make for less-readable code than alternatives. Still, it's perfectly legal and well-defined.) > > It's a massive amount of completely unnecessary overhead AND a > > difficult question of which parts belong in the closure and which > > parts belong as parameters, which means that this is nearly impossible > > to define usefully. > > I've given you two useful definitions. > > Its not clear what overhead you are worried about. > > Accessing variables in cells is almost as fast as accessing locals, but > even if they were as slow as globals, premature optimization is the root > of all evil. Globals are fast enough. > > Or are you worried about the memory overhead of the closures? The extra > cost of fetching and calling the functions when evaluating the defaults? > None of these things seem to be good reasons to dismiss the idea that > default expressions should be independent of the function body. The problem is that you're trying to reference cell variables in a closure that might not even exist yet, so *just in case* you might have nonlocals, you have to construct a closure... or find an existing one. Ill-defined and inefficient. > > That's not what the example shows. It shows that changing dunder > > attributes can do this. I'm not sure why you think that the > > implementation is as restricted as you imply. The assignment to > > __defaults_extra__ is kinda significant here :) > > Ah, well that is not so clear to people who aren't as immersed in the > implementation as you :-) Maybe, but I did think that the fact that I was assigning to a dunder should be obvious to people who are reading here :) > Messing about with function dunders can do weird shit: > > >>> def func(a=1, b=2): > ... return a+b > ... > >>> func.__defaults__ = (1, 2, 3, 4, 5) > >>> func() > 9 > > I wouldn't worry about it. > Good, so, not an abomination. (In your opinion. Others are, of course, free to abominate as they please.) ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ZO7QY33762WUCNGXULVBH6ZCSWOTHKXK/ Code of Conduct: http://python.org/psf/codeofconduct/