Jonathan Fine wrote:

Jeff Shannon wrote:

Jonathan Fine wrote:

Giudo has suggested adding optional static typing to Python.
(I hope suggested is the correct word.)
  http://www.artima.com/weblogs/viewpost.jsp?thread=85551

An example of the syntax he proposes is:
 > def f(this:that=other):
 >     print this


<snip>

I'm going to suggest a different use for a similar syntax.

In XML the syntax
 >  <element this:that='other'>
is used for name spaces.

Name spaces allow independent attributes to be applied to an
element.  For example, 'fo' attributes for fonts and layout.
XSLT is of course a big user of namespaces in XML.

Namespaces seems to be a key idea in allow independent
applications to apply attributes to the same element.
[...]
Here's an example of how it might work.  With f as above:
 > f(this:that='value')
{'that': 'value'}



I fail to see how this is a significant advantage over simply using **kwargs. It allows you to have multiple dictionaries instead of just one, that's all. And as you point out, it's trivial to construct your own nested dicts.


This argument could be applied to **kwargs (and to *args).  In other
words, **kwargs can be avoided using a trivial construction.

The use of *args and **kwargs allows functions to take a variable number of arguments. The addition of ***nsargs does not add significantly. Note that *args and **kwargs should always be used together, because to do otherwise would require the function caller to know details of the function's implementation (i.e. which arguments are expected to be positional and which must be keywords). Since we *want* the caller to not need to know this, then ***nsargs would always need to be used together with *args and **kwargs. (A function defined 'def f(***nsargs): ...' could not be called with 'f(1)'. This means that all you're gaining is an extra bag to put variable numbers of arguments in. The value here is that it maintains a parallel with *args and **kwargs when one allows 'namespaced' arguments -- if one allows that, then ***nsargs is required for consistency's sake, but it does not simplify anything by itself.


So really, we need to look at what gains we'd get from having 'namespaced' arguments. What's the real advantage here?

When using 'namespace' arguments, instead of standard keyword arguments, the function body would be given a dictionary instead of a set of local variables, right? 'def f1(arg1, arg2, arg3, arg4)' creates four names in locals(), where 'def f2(ns1:arg1, ns1:arg2, ns1:arg3, ns1:arg4) creates a single dict named ns1 in locals(), which contains four items (keyed on 'arg1', 'arg2', etc.), and 'def f3(ns1:arg1, ns1:arg2, ns2:arg3, ns2:arg4)' creates two dicts (ns1 and ns2) with two entries each.

Okay, now, let's take a look at how these functions will be used.

   f1(1, 2, 3, 4)
   f1(arg1=1, arg2=2, arg3=3, arg4=4)

Note that f1 doesn't care which of these methods of calling is employed -- both result in the same situation inside of f1().

So what's the intended way of calling f2()? I'd presume that it shouldn't care whether keywords or namespaces are specified, so that the following should all be equivalent:

   f2(1, 2, 3, 4)
   f2(1, 2, arg3=3, arg4=4)
   f2(1, 2, arg3=3, ns1:arg4=4)

Note that this doesn't *add* any utility. The function caller hasn't gained anything. Since arg4 is unambiguous regardless of whether it's referred to as arg4 or ns1:arg4, the only time that the caller has any reason to specify the namespace is if argnames within different namespaces clash -- that is, if we allow something like 'def f4(ns1:arg1, ns1:arg2, ns2:arg1, ns2:arg2)'.

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.

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. 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.

    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.

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.

Besides, Python already uses the concept of namespaces by mapping them to object attributes. [...]

Here, I don't understand. Could you give an example of two obvious ways of doing the same thing, should my suggestion be adopted?

My main point here is that 'namespace' is a term/concept that is already in use in Python, in different circumstances and using a completely different mechanism. Functions already *have* namespaces, whose contents can be inspected with locals() and modules have namespaces that can be accessed through globals().


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.

Jeff Shannon
Technician/Programmer
Credit International


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

Reply via email to