Is this PEP-able? (syntax for functools.partial-like functionality)


F

Fábio Santos

I had an idea for a handy syntax which I thought of while designing a
language for fun. This would be a syntax for creating, from a function, a
function which is just like it but with some parameters pre-filled. The
usage is much like functools.partials, and my proposed syntax is like this:

def spam(a, b, c):
...

spam_with_defaults = spam{1, 2}

Now calling spam_with_defaults is just like calling spam, but it only needs
one argument since a and b were set to 1 and 2, respectively.

spam_with_defaults(3) # same as calling spam(1, 2, 3)

This would also work with keyword arguments, E.G. spam{c=3} would return a
callable which would only need the arguments a and b.

This is just the plain old functools.partial functionality, but of course I
won't stop here. Still on our spam function:

spam_require_b = spam{2, *, 3}
spam_require_ab = spam{*, 3}

spam_require_a(1) # same as spam(1, 2, 3)
spam_require_ab(1, 2) # same as above

The * sign means that the function takes positional arguments which will be
added in place of the star. This is how we would do spam_require_b in pure
python:

def spam_require_b(*args, **kwargs):
return spam(*([1] + args + [2]), **kwargs)

Or, since we know it's only one argument,

spam_require_b = lambda b: spam(1, b, 3)

I also propose unpacking:

spam_unpacking = spam{1, (*, *)}
c = map(spam_unpacking, some_dict.items())

(Although this syntax isn't final), and receiving specific keyword
arguments.

spam_kw = spam{a, b, c=*}

The use cases this is intended to serve are mostly iteration related. There
is the case for being good plumbing for functions such as map, sorted,
filter and itertools.takewhile.

lines = filter(str.startswith{*, '#'}, open('file.cfg'))
lines = filter(bool, map(str.strip, lines))
config = dict(map(str.split{*, '=', 1}, lines))

A secondary use case is the creation of aliases.

def baz(self, callback):
respond = callback{instance=self}
...

What do you think?

PS: yes, I realized that I am proposing the addition of braces to the
language syntax.
 
Ad

Advertisements

S

Steven D'Aprano

I had an idea for a handy syntax which I thought of while designing a
language for fun. This would be a syntax for creating, from a function,
a function which is just like it but with some parameters pre-filled.
The usage is much like functools.partials, and my proposed syntax is
like this:

def spam(a, b, c):
...

spam_with_defaults = spam{1, 2}


Handy it may be, but why is this usage important enough to deserve new
syntax? The barrier to entry for new syntax is very high. For example,
Enums are a common, and standard, feature in many programming languages.
Enums will be introduced to Python in 3.4, but even they don't get
special syntax.
 
F

Fábio Santos

Handy it may be, but why is this usage important enough to deserve new
syntax? The barrier to entry for new syntax is very high. For example,
Enums are a common, and standard, feature in many programming languages.
Enums will be introduced to Python in 3.4, but even they don't get
special syntax.

I do realize that syntax in python is practically written in stone, but I
have seen changes come by if they have good reasons. For example,
keyword-only argument syntax was added.

I suggested this because I thought it would be the most practical way to
create partial functions. Lambda is too verbose and kind of ugly, and its
purpose is not to create partials, functools.partial does not allow
argument "insertion" (the star thing) nor unpacking.

Maybe I saw this as common usage but it is really a special case. I see
myself needing this kind of advanced partials _very_ often.
 
Ad

Advertisements

F

Fábio Santos

I think that if you're doing argument insertion, then your partial
application is overly complex in the way that it modifies the original
argspec. You should be looking at it as a new function, not as a
partial application that prevents you from easily supplying a doc
string for it. If you want to specify an arbitrary combination of
arguments in your partial application, you can already do that using
keyword arguments. The exception would be if the function being
partially applied takes a *args argument and you want to specify some
of those arguments using partial without specifying earlier arguments.
That seems like an abuse of partial application to me.

Tuple unpacking in function signatures was a feature in Python 2, and
it was intentionally removed in Python 3, so this is very unlikely to
happen. See PEP 3113. Besides which, the syntax you suggest for this
is unintuitive.

spam_unpacking = spam{1, (*, *)}

To me, this reads that spam_unpacking will take two positional
arguments that will be packed into the second argument sent to spam
(b), and that the third argument to spam (c) is not specified here. I
don't think that's what you intended.

No, that syntax was meant to take a tuple and break it into two arguments.
It does read terribly.

Anyway, good points were made and I have seen the light. I've changed my
mind. This is a bad idea.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top