On Wed, Oct 12, 2011 at 11:48 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Thu, Oct 13, 2011 at 2:45 PM, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> wrote:
I really like the proposal, although I would be interested to see if anyone can bikeshed up keywords that might be better than : or @… :-)
I do have some questions about it. Does using @ instead of the name defined below make the implementation easier? In other words, would this cause a NameError on normalize because normalize isn't defined in the local namespace?--
:sorted_list = sorted(original, key=normalize) def normalize(item): …
Or is the @ just for brevity? I assume the point is that it's not just brevity, but you have to use the @ in order to make the implementation straightforward.
The brevity and highlighting the "forward reference" are actually the primary motivation, making the implementation easier (which it does do) is a nice bonus.
When I first started writing up the PEP, I was using a syntax more directly inspired by PEP 3150's given clause:
:sorted_list = sorted(original, key=@) given (item): …
There were a few problems with this: 1. It pushes the signature of the callable all the way over to the RHS 2. The callable is actually anonymous, so @.__name__ would always be "<given>". That's annoying for the same reason as it's annoying in lambda expressions. 3. It doesn't provide any real clues that the body of the statement is actually a nested function
So then I thought, "well what if I use 'def' instead of 'given' as the keyword"? At that point, it was only a short leap to realising that what I wanted was really close to "arbitrary simple statement as a decorator". So I rewrote things to the format in the posted PEP:
:sorted_list = sorted(original, key=@) def normalise(item): …
Now that the nested function is being given a name, I realised I *could* just refer to it by that name in the block prefix. However, I left it alone for the reasons I mention above:
1. It highlights that this is not a normal name reference but something special (i.e. a forward reference to the object defined by the statement) 2. The fact that references are always a single character makes it easier to justify giving the function itself a nice name, which improves introspection support and error messages. In genuine throwaway cases, you can always use a dummy name like 'f', 'func', 'g', 'gen' or 'block' or 'attr' depending on what you're doing. 3. Multiple references don't make the block prefix unwieldy (although the use cases for those are limited) 4. It does make the implementation easier, since you don't have to worry about namespace clashes - the function remains effectively anonymous in the containing scope.
I like it a lot better as a symbol than as an identifier. It visually pops out.
It would be possible to extend the PEP to include the idea of allowing the name to be omitted in function and class definitions, but that's an awful lot of complexity when it's easy to use a throwaway name if you genuinely don't care.
easy: :sorted_list = sorted(original, key=@) def _(item): … -eric
Just like PEP 3150, all of this is based on the premise that one of the key benefits of multi-line lambdas is that it lets you do things in the *right order* - operation first, callable second. Ordinary named functions force you to do things the other way around. Decorator abuse is also a problematic approach, since even though it gets the order right, you end up with something that looks like an ordinary function or class definition but is actually nothing of the sort.
Oh, I'll also note that the class variant gives you the full power of PEP 3150 without any (especially) funky new namespace semantics:
:x = property(@.get, @.set, @.delete) class scope: def get(self): return __class__.attr def set(self, val): __class__.attr = val def delete(self): del __class__.attr
Cheers, Nick.
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas