[Python-ideas] Tweaking closures and lexical scoping to include the function being defined
Nick Coghlan
ncoghlan at gmail.com
Wed Sep 28 12:45:08 CEST 2011
On Wed, Sep 28, 2011 at 6:26 AM, Paul Moore <p.f.moore at gmail.com> wrote:
> On 28 September 2011 08:10, Paul Moore <p.f.moore at gmail.com> wrote:
>> That either validates the default-argument hack as a valid response to
>> a specific requirement, or suggests some syntax added to the function
>> definition line. Or of course, just go with a normal (nested function)
>> closure.
>
> Taking a step back from all this, using an explicit closure to
> implement the counter example being used throughout this thread looks
> something like this:
>
>>>> # I thought apply still existed in some module. Never mind, can't find it so I'll reimplement a quick version here...
>>>> def apply(f): return f()
> ...
>>>> @apply
> ... def counter():
> ... n = 1
> ... def inner():
> ... nonlocal n
> ... print(n)
> ... n += 1
> ... return inner
> ...
>>>> counter()
> 1
>>>> counter()
> 2
>>>> counter()
> 3
>
> To be honest, that doesn't actually look that bad. Explicit is better
> than implicit and all that, and the boilerplate doesn't really bother
> me that much.
>
> The real annoying boilerplate here is the "def dummy_name()...return
> dummy_name" bit. But fixing that leads us directly back to the
> perennial discussion on anonymous multiline functions...
Actually, there are some additional aspects that annoy me:
- the repetition of the variable name 'n'
- the scope of the variable name is wrong (since we only need it in
the inner function)
- indentation matters (cf try/except/finally)
- hidden signature for the actual function
- hoops to jump through to get decent introspection values (e.g. __name__)
I find the following 5 line toy example:
from threading import Lock
def global_counter() [n=1, lock=Lock()]:
with lock:
print(n)
n += 1
Far more readable than the 10-line closure equivalent:
from threading import Lock
@apply # I sometimes wonder if we should bring this back in functools...
def global_counter():
n = 1
local = Lock()
def global_counter():
with lock:
print(n)
n += 1
return global_counter
Or the 10-line 7-self class equivalent:
from threading import Lock
@apply
class global_counter:
def __init__(self):
self.n = 1
self.lock = Lock()
def __call__(self):
with self.lock:
print(self.n)
self.n += 1
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
More information about the Python-ideas
mailing list