On Fri, 05 Nov 2010 12:17:00 +0000, Mark Wooding wrote: >> #1 Most default values are things like True, False, None, integer or >> string literals. Since they're literals, they will never change, so you >> only need to set them once. > > Right; so a half-decent compiler can notice this and optimize > appropriately. Result: negligible difference.
You're right, of course, a sufficiently smart compiler could do this. It might even be worth doing in a language that otherwise re-initialises function defaults on every call. But such optimizations don't happen for free, they have costs: somebody has to write it, debug it, maintain it, deal with the added complexity. Python -- at least CPython -- tends to go for the simplest compiler that will work. Perhaps the biggest cost is that now your language has inconsistent semantics: some function defaults are set on every call, and some are set once, when the function is defined, and the choice between the two happens via "magic" -- the compiler decides what to do, you don't. I have mixed feelings about compiler optimizations. Things like constant folding seems to be both harmless and useful, but other optimizations not so much. It sets up a discrepancy between what the source code does and what the compiled code does, and in the case of (say) floating point code, can introduce *serious* bugs. So while I like the idea of compiler optimizations is principle, in practice I tend to think they should be avoided. >> #2 It's easy to get default values to initialise on function call in a >> language that uses initialisation on function definition semantics: >> just move the initialisation into the function body. Python has a >> particularly short and simple idiom for it: >> >> def f(x=None): >> if x is None: >> x = some_expression() > > That's actually rather clumsy. You think so? I think it's quite simple, obvious and neat. > Also, it's using in-band signalling: > you've taken None and used it as a magic marker meaning `not supplied'. > Suppose you want to distinguish /any/ supplied value from a missing > argument: how do you do this /correctly/? > > The approaches I see are (a) to invent some unforgeable token That's the simplest way. _sentinel = object() def f(x=_sentinel): if x is _sentinel: x = some_expression() > or (b) > emulate the argument processing by rifling through * and ** arguments. > > Solution (a) looks like this: > > _missing = ['missing'] > def foo(arg = _missing): > if arg is _missing: arg = ... > ... A curious choice for the sentinel value. We're not using Python 1.5 any longer :) > But now _missing is kicking around in the module's namespace. Er, yes. That's what namespaces are for. >> But if the situations were reversed, it's hard to get the DID >> semantics: >> >> def f(x=None): >> if x is None: >> global _f_default_arg >> try: >> x = _f_default_arg >> except NameError: >> _f_default_arg = x = default_calculation() > > Ugh. This is artificially awful and doesn't correspond to existing > Python DID semantics. [snip] Yes, you're right, those are good points and I stand corrected. -- Steven -- http://mail.python.org/mailman/listinfo/python-list