jacek.generowicz at cern.ch
Sun Jul 11 10:30:37 CEST 2004
On 11 Jul 2004, at 06:17, Robert Brewer wrote:
> I asked:
>> Which immediately brings up the question: why not use a
>> class instead?
> to which Jacek replied:
>> a) The closure is faster, and my inner loops consist almost
>> entirely of them.
>> b) The closure is much clearer, shorter and more convenient.
>> I have so many of these things that using the class version
>> would make the code a real mess.
>> c) The closure can be stuck on to a class, and works as a
>> method (bound & unbound and all that).
>> d) The outer function which creates the closure can be a method
>> of a class.
>> Probably quite a few more ...
> I have to wonder about 'b'; does anyone have an example where a
> closure is clearer?
Well, I guess that you can't argue with it being shorter and more
convenient: with the closure you do away with the constructor (in which
you explicitly enclose the variables you need), and you don't need to
use "self" to refer to the enclosed variables in the closure itself. If
you have only one closure, referring to the state, then the class-based
version can use __call__ to simulate function-call syntax, but if the
state is shared between more than one closure, you need to prepend
calls with some instance name. All this is just simple fact: the
closure version is more concise.
Whether it's clearer, is, of course, a matter of opinion. If Python
formed your programming mind, and you believe in the third line of the
Zen of Python to the extreme, then you will probably find the
class-based version clearer; if you are comfortable with lexical
scoping, then you will probably find the closure clearer.
Personally, I (would) find it annoying to have to install the plumbing
myself every time. In the application I'm writing at the moment, I have
many functions which generate other functions. When I need the
generated function to have some state, it requires exactly zero extra
effort on my part: I merely refer to the variable just like any other,
and it works. If I had to use the class, I would need to rewrite the
code, add the plumbing, the generated function would require extra work
before it could be used as a method (and it's impossible before Python
2.3[*]), and I wouldn't be able to generate it in methods of a class.
Sorry, I'm not going to come up with code samples showing that the
closure is clearer, because the class-based version is (to me) so
inconvenient that I just can't be bothered to even try to make it work
in the interesting situations.
> Yes, this means you have to invent syntax to mutate the cell variable
> if you want to discuss it. ;) Having perused previous discussions, I
> think I'm favoring the 'lexical'/'outer' keyword idea; seems we're
> just waiting for someone to write a PEP...
A "lexical" keyword would (sorry, I haven't googled for the
discussions, so I'm guessing what was proposed: essentially the same as
the "global" keyword) certainly be least surprising to Pythoneers, but
it would require the introduction of a new keyword, which means any
such proposal is likely to be rejected.
An alternative solution, coming from the "Classes are Python's
mechanism for sharing state" school of thought, could be to endow
classes and instances with the function descriptors which are
responsible for binding methods - not sure what would happen when you
want an instance method to be bindable again ... or some approach based
on subclassing FunctionType.
But the bottom line is, when I want functions which happen to have some
state, I really don't want to have to go to the hassle of simulating
them with classes. Closures are an extremely, simple, easy, clear and
efficient way of adding state to a functon. It's just a pity that that
state is not mutable.
[*] I may be wrong about this, as I don't actually use the class
version myself, so I don't really care ... but I think there was an
issue with MethodType not being instantiable before Python2.3 (or
something along those lines, at least). IIRC, Michael Hudson pointed
out to me that something which I assumed did not work (because it
didn't in 2.2), actually did work (in 2.3) ... all on c.l.py.
More information about the Python-list