
Jim Jewett wrote:
On Mon, Jun 13, 2011 at 10:33 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Nick Coghlan wrote:
On Mon, Jun 13, 2011 at 5:11 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Function parameters should be kept for actual arguments, not for optimizing name look-ups.
Even the bind-it-now behavior isn't always for optimization; it can also be used as a way of forcing stability in case the global name gets rebound. That is often an anti-pattern in practice, but ... not always.
Acknowledged. But whatever the purpose, my comment still stands: function arguments should be used for arguments, not for their side-effect of injecting a local variable into the function namespace.
The problem with injecting locals in the parameter list is that it can only happen at write-time. That's useful, but there's a major opportunity being missed: to be able to inject at runtime. You could add test mocks, optimized functions, logging, turn global variables into local constants, and probably things I've never thought of.
Using the function object as a namespace (largely) gets around that, because you can use a with statement to change the settings temporarily.
You mean something like this? with make_logging_len() as len: x = some_function_that_calls_len() That's fine for some purposes, but you're still modifying global state. If some_function_that_calls_len() calls spam(), and spam() also contains a call to len, you've unexpectedly changed the behaviour of spam. If that's the behaviour that you want, fine, but it probably isn't. There are all sorts of opportunities for breaking things when patching globals, which makes it somewhat of an anti-pattern. Better to make the patched version a local.
Here's one use-case to give a flavour of what I have in mind: if you're writing Unix-like scripts, one piece of useful functionality is "verbose mode". Here's one way of doing so:
[A verbose mode -- full example below, but the new spelling here at the top]
Just replace:
def do_work(args): hook("doing spam") spam() hook("doing ham") ham()
with:
def do_work(args): __function__.hook("doing spam") spam() __function__.hook("doing ham") ham() [...] (The reason this requires a variant of 3130 is that the name do_work may itself be rebound, so do_work.hook isn't a reliable pointer.)
Ah, that's why it doesn't work for me! :) Even if it did work, you're still messing with global state. If two functions are using do_work, and one wants a print hook, and the other wants a logging hook (or whatever), only one can be satisfied. Also this trick can't work for optimizations. A call to do_work.hook requires a global lookup followed by a second lookup in the function object namespace, which is not as fast as using a local. -- Steven