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