What could 'f(this:that=other):' mean?

Jonathan Fine jfine at pytex.org
Fri Jan 7 16:57:48 EST 2005


Jeff Shannon wrote:
> Jonathan Fine wrote:

<snip>

> The use of *args and **kwargs allows functions to take a variable number 
> of arguments.  The addition of ***nsargs does not add significantly.  

I've posted usage examples elsewhere in this thread.
I think they show that ***nsargs do provide a benefit.
At least in these cases.

How significant is another matter.  And how often do they occur.

<snip>

> Now, though, we've lost the ability to specify *only* the argname and 
> not the namespace as well -- that is, you *cannot* call f4 with keywords 
> but not namespaces.  From the caller's vantage point, this means that 
> they need to know the full namespace spec of the function, which makes 
> it no different than simply using longer (but unique) keyword names.

This is a major point.

 From the caller's point of view:
   fn_1(x_aa=1, x_bb=2, y_aa=3)
   fn_2(x:aa=1, x:bb=2, y:aa=3)
are very similar.  Replace '_' by ':'.

So you are saying, I think, 'what does this buy the caller'.

Well, by using ':' the namespacing is explicit.
   fn_3(my_path, my_file, any_file)
Is this an example of implicit namespacing? (Rhetorical question.)

Explicit is better than implicit, I'm told (smile).


> So, we can see that allowing namespaces and ***nsargs doesn't add any 
> utility from the caller's perspective.  How much does the callee gain 
> from it?
> 
> Well, the following functions would be equivalent:
> 
>     def g1(arg1, arg2, arg3, arg4):
>         ns1 = {'arg1':arg1, 'arg2':arg2, 'arg3':arg3, 'arg4':arg4}
>         return ns1
>     def g2(ns1:arg1, ns1:arg2, ns1:arg3, ns1:arg4):
>         return ns1
> 
> You might say "Wow, look at all that repetetive typing I'm saving!" But 
> that *only* applies if you need to stuff all of your arguments into 
> dictionaries.  

This is a key point.  But there's more to it.

Namespace arguments are good if you have to divide the arguments into
two or more dictionaries.  Based on the namespace prefix.

> I suspect that this is a rather small subset of 
> functions.  In most cases, it will be more convenient to use your 
> arguments as local variables than as dictionary entries.

This is a matter of usage.  If the usage is very, very small, then
what's the point.  If the usage is very, very large, then the case
is very strong.

>     def gcd1(a, b):
>         while a:
>             a, b = b%a, a
>         return b
> 
>     def gcd2(ns1:a, ns1:b):
>         while ns1['a']:
>             ns1['a'], ns1['b'] = ns1['b']%ns1['a'], ns1['a']
>         return ns1['b']
> 
> Speaking of repetetive typing.... :P
> 
> Besides, another function equivalent to the above two would be:
> 
>     def g3(arg1, arg2, arg3, arg4):
>         ns1 = locals()
>         return ns1
> 
> ....which is quite a bit less repetetive typing than the 'namespace' 
> version.

These are not good examples for the use of namespacing.
And the results are horrible.

So this is an argument _in favour_ of namespacing.

Namely, that it _passes_ "there should be one (and preferably only one) 
obvious way to do things" test (quoted from your earlier message).


> So, you're looking at making the function-calling protocol significantly 
> more complex, both for caller and callee, for the rather marginal gain 
> of being able to get arguments prepackaged in a dictionary or two, when 
> there already exist easy ways of packaging function arguments into a 
> dict.  Given the deliberate bias against adding lots of new features to 
> Python, one needs a *much* better cost/benefit ratio for a feature to be 
> considered worthwhile.

I believe that we _agree_, that it is a matter of cost/benefit ratio.

My opinion is that studying usage examples is a good way of evaluating
this ratio.  Which is why I have posted some favourable usage examples.

<snip>

> I'd note also that the usage you're drawing from, in XML/XSLT, isn't 
> really comparable to function parameters.  It's a much closer parallel 
> to object attributes.  Python *does* have this concept, but it's spelled 
> differently, using a '.' instead of a ':'.  In other words, the XML 
> fragment you give,
> 
>     <element this:that='other'>
> 
> .... would be more appropriate to render in Python as
> 
>     e = Element()
>     e.this.that = 'other'
> 
> It's quite reasonable to suppose that some object of type Element may 
> have a set of font attributes and a set of image attributes, and that 
> some of these may have the same name.  Python would use font objects and 
> image objects instead of 'namespaces' --
> 
>     e.font.size = '14pt'
>     e.image.size = (640, 480)
> 
> So while these namespaces are probably a great thing for XML, XSLT, 
> they're not very useful in Python.  Which, given the rather different 
> goals and design philosophies behind the languages, shouldn't really be 
> much of a surprise.

It may be better, in some cases, to write

   fp = FontParams()
   fp.size = 14
   fp.slope = 0.1
   f(this=this, font_params=fp)

But not, I think, if the definition of f is something like:

   def f(this, font_params):

       fpd = font_params.__dict__
       font_function(**fpd)

(Assuming that font_function has got it right.)


Prompted by your post, the idea of writing
   f(this.that='other')
instead of
   f(this:that='other')
came to my mind.

At first I did not like it, but now it is growing on me a little.

However, that is a syntactic issue.  Which falls if the costs do
not justify the benefits.


And here, I think, the key question is this:

How often do you have to divide the arguments to a function into
two or more dictionaries?

Which can be answered by studying usage.


--
Jonathan
http://www.pytex.org




More information about the Python-list mailing list