Verbose and flexible args and kwargs syntax

E

Eelco Hoogendoorn

No more so than any other form of punctuation. Plus and minus + - may be
so common that just about everyone knows it, but how about | == @ % and
even . (dot)? None of these things will be obvious to newbies who have
never programmed before. Oh well.
Some things you just have to learn.


Yes, some things you just have to learn. Nonetheless, I strongly prefer
explicit logical operators over |, would much rather have 'equals'
instead of ==, which is stylistic in line with 'is' and explicitly
distinguishes between equality and identity comparisons. As for %; it is
entirely unclear to me why that obscure operation ever got its own
one-character symbol. Ill take 'mod', or even better, 'modulus' any day
of the week.

The dot is clearly quantitatively in another realm here. 90% of typical
python code is attribute accesses. The dot is entirely unambigious and
cannot be mistaken for anything else. It reads like a book.
It's a judgement call as to where a language divides "cryptic punctuation
line noise" and "useful short operators", and in my opinion * and ** tuple
and dict unpacking fall strongly on the "useful short operators" side.
Your opinion may differ, but luckily for me, the BDFL agrees with me :)

I also agree that it is a value judgement as to which constructs get
their own cryptic symbols and which do not, but the are some reasonable
guidelines we should be able to agree upon. Obscure operations should
not reserve any of the few available characters. Furthermore, the
language should not just be formally consistent, but also easy to grasp
at a glance, without deciphering subtle semantics of a blurb of weird
characters. (some programming languages obviously disagree, but python,
as far as I am allowed to speak for it, does not). And most importantly,
if you cant come up with a terse syntax that does everything you want to
do, the terse syntax should at best be an alternative to the verbose one.

It is also misleading because args are not collected into a list, but
into a tuple.

In case you wanted a tuple youd write tuple(args), obviously. Exactly that added flexibility is half of my case in favor. Why shouldnt it be a list when I want it to?


Worse, it suggests that one should be able to generalise to
something like this:
def func(parg, str(args), int(kwargs), my_func(more_args)):
which is incoherent.

Sorry, but I dont get this point at all. Does ** suggests one should be
able to generalize to ***? The rules are the rules.



The real questions, in my mind, are:

1) How useful is this added flexibility? Not insanely, but I can see it
making a lot of code significantly more clean.

And:

2) How fundamental is collection packing/unpacking? One can easily argue
that it is indeed quite fundamental and therefore deserves its own terse
symbols, I feel. However, if more flexibility is indeed deemed
desirable, such terse syntax quickly gives way to a more verbose one.
Can you come up with some terse symbols that will be able to express all
of the below and dont make you wish you hadnt rather typed out the names?

head, tuple(tail) = iterable
head, list(tail) = iterable
head, str(tail) = somestring
head, generator(tail) = mygenerator

And so on.

If not, one has to admit that functionality is being sacrificed on the
alter of terseness, which seems like a raw deal to me.
 
S

Steven D'Aprano

Yes, some things you just have to learn. Nonetheless, I strongly prefer
explicit logical operators over |, would much rather have 'equals'
instead of ==, which is stylistic in line with 'is' and explicitly
distinguishes between equality and identity comparisons.

No more, or less, explicit than the difference between "==" and "is".

As for %; it is
entirely unclear to me why that obscure operation ever got its own
one-character symbol. Ill take 'mod', or even better, 'modulus' any day
of the week.

Modulo is hardly an obscure operation. "What's the remainder...?" is a
simple question that people learn about in primary school.

And you can blame C for the use of % instead of mod or modulo.

The dot is clearly quantitatively in another realm here. 90% of typical
python code is attribute accesses.

I can't imagine what sort of Python code you have seen that you consider
90% attribute access "typical". I've just run the Python tokenizer over
my startup.py file, and I get these results:

{'COMMENT': 24, 'DEDENT': 29, 'NL': 46, 'NAME': 256, "':'": 30,
'NEWLINE': 83, "'-'": 1, 'NUMBER': 1, "'['": 1, "','": 17, "')'": 37,
"'('": 37, "'%'": 2, "'.'": 48, "'=='": 1, "'*'": 1, 'INDENT': 29, "']'":
1, "'='": 28, 'ENDMARKER': 1, 'STRING': 19}

That gives attribute access being a little less than 7% of the source
code. For the decimal module, the figure is a little less than 5%.

The dot is entirely unambigious and
cannot be mistaken for anything else. It reads like a book.

The dot can be easily mistaken for a comma, or for a bit of grit on the
monitor, especially at smaller type sizes, or for those with poor
eyesight.


[...]
In case you wanted a tuple youd write tuple(args), obviously. Exactly
that added flexibility is half of my case in favor. Why shouldnt it be a
list when I want it to?

What sort of list? A built-in list, or whatever sort of object you get
when you call the thing currently bound to the name "list"?

If you can supply any function at all, what happens if I write this:

def func(parg, dict(foo), list(bar)): ...

How about this?

def func(parg, myfunc(x)): ...

What is x now? Should Python try to accumulate arguments by position, or
by keyword, or try both and hope one will succeed? Which order should it
try first?

I believe that your proposal leads to an over-generalisation "call
arbitrary functions when handling parameter lists". I don't believe you
need this added complication. If you want to your var args as a list,
call list(args) inside your function.

Sorry, but I dont get this point at all. Does ** suggests one should be
able to generalize to ***? The rules are the rules.

You have missed that the generalization is not just to multiple "chunks"
of arguments, but also to arbitrary functions. I thought that both ideas
were equally incoherent, but ironically you suggest about that you should
be able to call arbitrary functions: tuple, list, attrdict. What else?
str? int?

The real questions, in my mind, are:

1) How useful is this added flexibility? Not insanely, but I can see it
making a lot of code significantly more clean.

I don't. I see it making a small amount of code more verbose and less
clean.

And:

2) How fundamental is collection packing/unpacking? One can easily argue
that it is indeed quite fundamental and therefore deserves its own terse
symbols, I feel.

In Python, tuple unpacking and packing (actually applies to any
collection, not just tuples) is *very* fundamental. That's why we can do
things like this:

a, b, c = my_list
x, y = y, x
However, if more flexibility is indeed deemed
desirable, such terse syntax quickly gives way to a more verbose one.
Can you come up with some terse symbols that will be able to express all
of the below and dont make you wish you hadnt rather typed out the
names?

head, tuple(tail) = iterable

In Python 3, that is spelled:

head, *tail = iterable
tail = tuple(tail)

head, list(tail) = iterable

head, *tail = iterable

head, str(tail) = somestring

This is ambiguous, I'm not sure what exactly you expect to get as the
string. It could arguable be any of:

tail = ''.join(map(repr, tail))
tail = ''.join(map(str, tail))
tail = str(tail)

or even

head, tail = somestring[0], somestring[1:]

head, generator(tail) = mygenerator

And this is most easily spelled:

head, tail = next(mygenerator), mygenerator
 
G

Gregory Ewing

Steven said:
Modulo is hardly an obscure operation. "What's the remainder...?" is a
simple question that people learn about in primary school.

Well, sort of. The way I remember it, the remainder was just
something that fell out as a side effect of division -- the
annoying bit left over that you didn't know what to do with.
We weren't taught to think of "finding the remainder" as
a distinct operation that's useful in its own right. Once
we were taught to do proper division with decimal points
and everything, the concept of a remainder seemed to get
discarded and was never mentioned again.

A couple of years later we were briefly introduced to the
concept of modulo arithmetic, but as far as I remember, the
connection with division and remainders wasn't pointed out.
Also, it was presented in a very abstract way, and I couldn't
see any practical application for it at the time. (At that
age, it hadn't occurred to me that some of the stuff we
were being shown might be just pure mathematics for its own
sake, and I was often thinking to myself, "Why am I being
taught this?")

It wasn't until much later when I got into programming that
I began to see all the connections and applications. For
people who don't become programmers, I suspect they never
have much use for remainders in everyday life.

Now, in a desperate attempt to stop rambling and get back
The dot is clearly quantitatively in another realm here.
[/QUOTE]

Also it has almost unchallenged supremacy as the attribute
access operator in just about every language having some
form of struct concept, going back to around Algol 68.
Spelling of the mod operator is much more variable.
{'COMMENT': 24, 'DEDENT': 29, 'NL': 46, 'NAME': 256, "':'": 30,
'NEWLINE': 83, "'-'": 1, 'NUMBER': 1, "'['": 1, "','": 17, "')'": 37,
"'('": 37, "'%'": 2, "'.'": 48, "'=='": 1, "'*'": 1, 'INDENT': 29, "']'":
1, "'='": 28, 'ENDMARKER': 1, 'STRING': 19}

That gives attribute access being a little less than 7% of the source
code.

However, it's clearly the most commonly used *operator* by
a large margin.
The dot can be easily mistaken for a comma,

Not in my code, because I always put a space after a comma,
and never after an attribute-access dot. (And if you can't
tell whether there's a space there or not, you need a
bigger font or better glasses. :)

Also, dots sit nicely under my little finger where they're
easy to type. I like dots. Dots are a great goodness.
 
J

Jussi Piitulainen

Eelco said:
As for %; it is entirely unclear to me why that obscure operation
ever got its own one-character symbol. Ill take 'mod', or even
better, 'modulus' any day of the week.

The modulus is not the result but one of the arguments: when numbers x
and y are congruent modulo n (stated in terms of the modulo operation:
x mod n = y mod n), the modulus is n. A word for x mod n is remainder.

I agree about the obscurity of using the percent sign as the operator.

A quick google suggests that your use of 'modulus' is now popular
among programmers. Past experience in mathematics newsgroups tells me
that some mathematicians do not accept the existence of any remainder
operator at all. Honest. (I see them but I cannot understand them.)
 
E

Eelco

By the way...

Is there any particular reason why some of my replies do not show up
on groups.google, and some of them do not show up on mail.python.org?
Sorry to annoy people with reposting, but im going to be forced to do
some of that until this is cleared up....
 
E

Eelco

The modulus is not the result but one of the arguments: when numbers x
and y are congruent modulo n (stated in terms of the modulo operation:
x mod n = y mod n), the modulus is n. A word for x mod n is remainder.

I agree about the obscurity of using the percent sign as the operator.

A quick google suggests that your use of 'modulus' is now popular
among programmers. Past experience in mathematics newsgroups tells me
that some mathematicians do not accept the existence of any remainder
operator at all. Honest. (I see them but I cannot understand them.)

You are correct; the thing it computes is the remainder, not the
modulus. Nonetheless, 'x modulus y' is how it is put in natural
language, but I suppose math.remainder would be my preferred place to
put this.
 
J

Jussi Piitulainen

Eelco said:
You are correct; the thing it computes is the remainder, not the
modulus. Nonetheless, 'x modulus y' is how it is put in natural
language, but I suppose math.remainder would be my preferred place to
put this.

I think it's 'x modulo y', which matches 'x and y are congruent modulo
z', but now I fear that programming people have been developing a
different habit.
 
E

Eelco

No more, or less, explicit than the difference between "==" and "is".

== may be taken to mean identity comparison; 'equals' can only mean
one
thing. Of course 'formally' these symbols are well defined, but so is
brainf*ck
Modulo is hardly an obscure operation. "What's the remainder...?" is a
simple question that people learn about in primary school.


So is 'how much wood would a woodchucker chuck if a woodchucker could
chuck wood?'. But how often does that concept turn up in your code?
And you can blame C for the use of % instead of mod or modulo.

I didnt know one of Python's design goals was backwards compatibility
with C.
I can't imagine what sort of Python code you have seen that you consider
90% attribute access "typical". I've just run the Python tokenizer over
my startup.py file, and I get these results:

Yes, that was a hyperbole; but quite an often used construct, is it
not?
If you can supply any function at all, what happens if I write this:


You cannot; only constructors modelling a sequence or a dict, and
only
in that order. Is that rule clear enough?
I believe that your proposal leads to an over-generalisation "call
arbitrary functions when handling parameter lists".

I hope the above clears that up. It is as much about calling
functions
as ** is about raising kwargs to the power of.
I don't believe you
need this added complication. If you want to your var args as a list,
call list(args) inside your function.

We dont strictly 'need' any language construct. Real men use
assembler,
right?

In Python 3, that is spelled:
head, *tail = iterable
tail = tuple(tail)

Yes, I know. How is that not a lot more verbose and worse than what I
have proposed in all possible ways?
head, tail = somestring[0], somestring[1:]

Well yes, splendid; we can do that with lists too since the dawn of
Python. What you are saying here in effect is that you think the
head/tail syntax is superfluous; that youd rather see it eliminated
than
generalized.
head, tail = next(mygenerator), mygenerator

Which again of course works, but is yet again of entirely different
form
than any of the above solutions, while conceptually doing the same
thing. Certainly, there is room for improved elegance here?
 
T

Terry Reedy

people who don't become programmers, I suspect they never
have much use for remainders in everyday life.

Huh? Funny you should say 'everyday'. It is now 10 o'clock. In 20 hours,
it will be (10+20) % 12 == 6 o'clock. It is now day 1 of the week. In 9
days it will be day (1+9) % 7 == 3 of the week. Mental calculations are
helped by the fact that (a+b) % c == a%c + b%c, so that would actually
be 1+2==3. Timekeeping is mostly remaindering, slightly obscured by
using 12 instead of 0.
 
T

Terry Reedy

Past experience in mathematics newsgroups tells me
that some mathematicians do not accept the existence of any remainder
operator at all.

Even though they carry hour/minute/second remindering devices on their
bodies and put year/month/day remaindering devices on their wall?
'Twould be strange indeed!
 
N

Nick Dokos

calculations are helped by the fact that (a+b) % c == a%c + b%c, so

As long as we understand that == here does not mean "equal", only
"congruent modulo c", e.g try a = 13, b = 12, c = 7.

Nick
 
A

Arnaud Delobelle

Huh? Funny you should say 'everyday'. It is now 10 o'clock. In 20 hours, it
will be (10+20) % 12 == 6 o'clock. It is now day 1 of the week. In 9 days it
will be day (1+9) % 7 == 3 of the week. Mental calculations are helped by
the fact that (a+b) % c == a%c + b%c

You mean (a + b) % c == (a%c + b%c) % c

:)
 
C

Chris Angelico

As long as we understand that == here does not mean "equal", only
"congruent modulo c", e.g try a = 13, b = 12, c = 7.

This is the basis of the grade-school "casting out nines" method of
checking arithmetic. Set c=9 and you can calculate N%c fairly readily
(digit sum - I'm assuming here that the arithmetic is being done in
decimal); the sum of the remainders should equal the remainder of the
sum, but there's the inherent assumption that if the remainders sum to
something greater than nine, you digit-sum it to get the true
remainder.

(Technically the sum of the digits of a base-10 number is not the same
as that number mod 9, but if you accept that 0 == 9, it works fine.)

ChrisA
 
C

Chris Angelico

You mean (a + b) % c == (a%c + b%c) % c

:)

It's just integer wraparound. Modulo 9 is the same as "render this
number in base 9 and take the last digit" (and printing a number in
base 9 would normally be done with mod 9 division), and most people
can wrap their heads around the way an odometer wraps around.

ChrisA
 
J

Jussi Piitulainen

Terry said:
Even though they carry hour/minute/second remindering devices on their
bodies and put year/month/day remaindering devices on their wall?
'Twould be strange indeed!

They recognize modular arithmetic but for some reason insist that
there is no such _binary operation_. But as I said, I don't understand
their concern. (Except the related concern about some programming
languages, not Python, where the remainder does not behave well with
respect to division.)
 
E

Eelco

They recognize modular arithmetic but for some reason insist that
there is no such _binary operation_. But as I said, I don't understand
their concern. (Except the related concern about some programming
languages, not Python, where the remainder does not behave well with
respect to division.)

They might not be willing to define it, but as soon as we programmers
do, well, we did.

Having studied the contemporary philosophy of mathematics, their
concern is probably that in their minds, mathematics is whatever some
dead guy said it was, and they dont know of any dead guy ever talking
about a modulus operation, so therefore it 'does not exist'.

Whatever you want to call the concept we are talking about, or whether
you care to talk about it at all, it is most certainly a binary
operation, since there are two arguments involved. There is no way
around that.
 
G

gene heskett

This is the basis of the grade-school "casting out nines" method of
checking arithmetic. Set c=9 and you can calculate N%c fairly readily
(digit sum - I'm assuming here that the arithmetic is being done in
decimal); the sum of the remainders should equal the remainder of the
sum, but there's the inherent assumption that if the remainders sum to
something greater than nine, you digit-sum it to get the true
remainder.

(Technically the sum of the digits of a base-10 number is not the same
as that number mod 9, but if you accept that 0 == 9, it works fine.)

ChrisA

And that is precisely the reason I have failed to understand why the 1-10
decimal system seems to have hung on for several hundred years when it is
clearly broken.

Cheers, Gene
--
"There are four boxes to be used in defense of liberty:
soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
My web page: <http://coyoteden.dyndns-free.com:85/gene>
Grub first, then ethics.
-- Bertolt Brecht
 
N

Nick Dokos

Jussi Piitulainen said:
They recognize modular arithmetic but for some reason insist that
there is no such _binary operation_. But as I said, I don't understand
their concern. (Except the related concern about some programming
languages, not Python, where the remainder does not behave well with
respect to division.)

They are probably arguing that it's uniquely defined only on ZxN and
that there are different conventions to extend it to ZxZ (the programming
languages problem that you allude to above - although I don't know what you
mean by "does not behave well wrt division"). See

http://en.wikipedia.org/wiki/Remainder

If you choose one convention and stick to it, it becomes a well-defined
binary operation. C99 goes one way, python goes a different way (and
mathematics textbooks generally go a third way) and they are all happy,
as long as they don't try to talk to each other (e.g., porting C99
programs to python unthinkingly leads to trouble - duh). It was
implementation dependent in old C (whatever the hardware would give
you), which predictably - with 20-20 hindsight - turned out to be a Very
Bad Idea.

Nick

PS Z = integers, N = non-negative integers
 
N

Nick Dokos

gene heskett said:
And that is precisely the reason I have failed to understand why the 1-10

It's not clear from the above what you mean by "that is presicely the reason":
what is "that"?
decimal system seems to have hung on for several hundred years when it is
clearly broken.

"broken" how?

Thanks,
Nick
 
I

Ian Kelly

You cannot; only constructors modelling a sequence or a dict, and
only
in that order. Is that rule clear enough?

The dict constructor can receive either a sequence or a mapping, so if
I write this:

def func(a, b, dict(c)):

what will I get? Probably I would want the equivalent of:

def func(a, b, **c):

but you seem to be saying that I would actually get the equivalent of this:

def func(a, b, *c):
c = dict(c)

Cheers,
Ian
 

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

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top