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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to