Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why does this (not) work?

0 views
Skip to first unread message

Skip Montanaro

unread,
Aug 19, 2003, 5:41:01 PM8/19/03
to

Michael> But I want to use the * to make life easier to read, so I tried:

>>>> ("test",)*3
Michael> ('test', 'test', 'test')
>>>> "%s - %s - %s" % ("test",)*3
Michael> Traceback (most recent call last):
Michael> File "<stdin>", line 1, in ?
Michael> TypeError: not enough arguments for format string
>>>>

That's because the % and * operators have the same precendence and group
left-to-right. The above expression is equivalent to

("%s - %s - %s" % ("test",))*3

To force the * operation to be evaluated first you need to add some parens:

("%s - %s - %s" % (("test",))*3)

which works as expected:

>>> "%s - %s - %s" % (("test",)*3)
'test - test - test'

If you're worried about readability of large format operations, I suggest
you consider using dictionary expansion, e.g.:

>>> "%(var)s - %(var)s - %(var)s" % {"var": "test"}
'test - test - test'

or more commonly:

>>> var = "test"
>>> "%(var)s - %(var)s - %(var)s" % locals()
'test - test - test'

Skip

Fredrik Lundh

unread,
Aug 19, 2003, 5:36:17 PM8/19/03
to
Michael C. Neel wrote:

> But I want to use the * to make life easier to read, so I tried:
>
> >>> ("test",)*3

> ('test', 'test', 'test')
> >>> "%s - %s - %s" % ("test",)*3

> Traceback (most recent call last):

> File "<stdin>", line 1, in ?

> TypeError: not enough arguments for format string
> >>>

>>> "%s - %s - %s" % ("test",)*3

is the same thing as

>>> ( "%s - %s - %s" % ("test",) ) * 3

but you really want

>>> "%s - %s - %s" % ( ("test",)*3 )

(your eval/str construct is just a really inefficient way to add
parentheses to an expression...)

</F>


Michael C. Neel

unread,
Aug 19, 2003, 5:43:27 PM8/19/03
to

> try this:
> >>> "%s - %s - %s" % (("test",)*3)
>
> why else would
> >>> 3 % 1 * 2
> print 0 instead of 1?
>
> Jeff

Hmmm, can't say I like what this implies. In one case % is shorthand
for a sprintf function, the other it's a mathematical expression at the
same level of precedence of * and /. But the sprintf version is
"granted" the precedence of the mathematical version? What's the logic
behind that?

Oh well,
Mike

Michael C. Neel

unread,
Aug 19, 2003, 5:25:19 PM8/19/03
to
I've got this string in which I need to sub in the same word several
times; i.e:

>>> "%s - %s - %s" % ("test","test","test")


'test - test - test'
>>>

But I want to use the * to make life easier to read, so I tried:

>>> ("test",)*3
('test', 'test', 'test')
>>> "%s - %s - %s" % ("test",)*3
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: not enough arguments for format string
>>>

Which seemed like a good idea, but after some playing around I found
that:

>>> eval(str(("test",)*3))


('test', 'test', 'test')

>>> "%s - %s - %s" % eval(str(("test",)*3))


'test - test - test'
>>>

Did work. Odd because:

>>> type(("test",)*3)
<type 'tuple'>
>>> type(eval(str(("test",)*3)))
<type 'tuple'>
>>>

Any idea why the tuple to str to tuple works and not the tuple straight?

Mike

Erik Max Francis

unread,
Aug 19, 2003, 6:56:47 PM8/19/03
to
"Michael C. Neel" wrote:

> Hmmm, can't say I like what this implies. In one case % is shorthand
> for a sprintf function, the other it's a mathematical expression at
> the
> same level of precedence of * and /. But the sprintf version is
> "granted" the precedence of the mathematical version? What's the
> logic
> behind that?

Very, very good logic. Consider the following expression:

left % middle * right

If we are to take your suggestion, and % is to have different precedence
based on whether or not it is being used as a string formatting
operation or as module division, then the precedence will be

left % (middle * right)

or

(left % middle) * right

depending on the type of `left'. That means that the precedence changes
based on the types of the arguments. That is disastrous in a dynamic
language like Python for both processing and readability, because now it
means the same expression can have widely different semantic meanings
based on their arguments. Having the same operator have different
precedence based on its operands would be disastrous.

--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \ A wise man never loses anything if he have himself.
\__/ Montaigne

Cliff Wells

unread,
Aug 19, 2003, 5:54:34 PM8/19/03
to
On Tue, 2003-08-19 at 14:25, Michael C. Neel wrote:
> I've got this string in which I need to sub in the same word several
> times; i.e:
>
> >>> "%s - %s - %s" % ("test","test","test")
> 'test - test - test'
> >>>
>
> But I want to use the * to make life easier to read, so I tried:
>
> >>> ("test",)*3
> ('test', 'test', 'test')
> >>> "%s - %s - %s" % ("test",)*3
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> TypeError: not enough arguments for format string
> >>>

> Any idea why the tuple to str to tuple works and not the tuple straight?

Because % has higher precedence than * (or rather they have equal
precedence and the expression is evaluated left to right) so the
expression becomes

("%s - %s - %s" % ("test",)) * 3

when what you meant was

"%s - %s - %s" % (("test",) * 3)

Here's another approach:

>>> def fjoin(sep, s, n):
... return sep.join(["%s"] * n) % ((s,) * n)
...
>>> fjoin(' - ', "test", 3)


'test - test - test'


Regards,

--
Cliff Wells, Software Engineer
Logiplex Corporation (www.logiplex.net)
(503) 978-6726 (800) 735-0555


Amit Patel

unread,
Aug 19, 2003, 7:34:17 PM8/19/03
to

I think the logic behind that is that the operator precedence is
assigned before we know what the types of the operands are. You could
argue that since we can see it's a string, we should change the
precedence. But then we'd get weird situations like

print "..." % (x,)*3

not being the same as

s = "..."
print s % (x,)*3

You could argue that a flow analyzer could figure out that s is a
string, etc., but that's going down a dangerous path -- the semantics
start depending on how sophisticated your Python compiler is. Aieee! :)

Some of the 'cute' syntax features of Python (% for strings, `` for
repr) have bitten me more than once. :(

- Amit

--
Amit J Patel, Computer Science Department, Stanford University
http://www-cs-students.stanford.edu/~amitp/
``Parkinson's Other Law: Perfection is achieved only
at the point of collapse.''

Michael C. Neel

unread,
Aug 19, 2003, 7:16:27 PM8/19/03
to
Maybe I should phrase the question a bit differently, more to the real
issue (I guess, it's all academic from here), why is the % operator used
both for string formatting and remainders?

To me:

>>>"%d" % 2 * 3
"222"

Is not what I expected nor wanted. I could however be alone in that
opinion. I'm selfish; as a programmer I don't really care about the
internals of the parser, I care about ease to code - that's what I like
about python, I don't spend time debugging syntax, but working on the
logic of the program.

It's probably not hard to guess I don't like ', '.join(list) either ;-)


thanks for all the replies though!
Mike

Erik Max Francis

unread,
Aug 19, 2003, 8:16:32 PM8/19/03
to
"Michael C. Neel" wrote:

> Maybe I should phrase the question a bit differently, more to the real
> issue (I guess, it's all academic from here), why is the % operator
> used
> both for string formatting and remainders?

It's got a lot of precedent behind it, so it's too late to change now.
At some point, someone decided it made sense, so it was done.

> To me:
>
> >>>"%d" % 2 * 3
> "222"
>
> Is not what I expected nor wanted. I could however be alone in that
> opinion.

The real question is: How likely is this issue going to be a real
problem? Why not just learn to use parentheses when you're doing
something tricky with the right argument to the string formatting
operator, and be done with it, problem solved?

--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE

/ \ Human salvation lies in the hands of the creatively maladjusted.
\__/ Dr. Martin Luther King, Jr.

Ben Finney

unread,
Aug 19, 2003, 8:04:19 PM8/19/03
to
On Tue, 19 Aug 2003 19:16:27 -0400, Michael C. Neel wrote:
> why is the % operator used both for string formatting and remainders?

Historical. Neither operation has an obvious character on the keyboard,
and the '%' did not already have an obvious application as an operator
for either strings nor numbers.

For numbers, I believe the '%' for modulus has been around at least as
early as C, probably in much earlier languages. Its visual similarity
to the division operator, which is strongly related to modulus, probably
helped the decision.

For strings, the C printf formatting codes use '%' as a format marker
within the string, so when Python adopted the same convention, it was a
good choice when Python needed to choose a character for the operator.

> >>>"%d" % 2 * 3
> "222"
>
> Is not what I expected nor wanted. I could however be alone in that
> opinion.

No, you're not alone in that -- I'd certainly expect the "2 * 3" to have
a tighter binding than, and to be evaluated before, the string
formatting.

I think it's even potentially a bug that the "2 * 3" is interpreted as a
string operation, since its arguments are clearly numeric.

However, this does reinforce a principle I learned from somewhere (maybe
Scott Meyers, maybe Steve McConnell, maybe someone else) with regard to
remembering operator precedence in different languages:

Addition and subtraction are equal precedence. Multiplication and
division are equal precedence. Use parentheses to make explicit any
other precedence assumptions.

Using that rule, your example becomes:

>>> "%d" % ( 2 * 3 )
'6'

No problem.


> It's probably not hard to guess I don't like ', '.join(list) either
> ;-)

Nor I. But I am finding the reminder of the immutability of the list to
be useful.

--
\ "What if the Hokey Pokey IS what it's all about?" -- Anonymous |
`\ |
_o__) |
Ben Finney <http://bignose.squidly.org/>

Duncan Booth

unread,
Aug 20, 2003, 4:17:30 AM8/20/03
to
Skip Montanaro <sk...@pobox.com> wrote in
news:mailman.106132935...@python.org:

> If you're worried about readability of large format operations, I suggest
> you consider using dictionary expansion, e.g.:
>
> >>> "%(var)s - %(var)s - %(var)s" % {"var": "test"}
> 'test - test - test'
>
> or more commonly:
>
> >>> var = "test"
> >>> "%(var)s - %(var)s - %(var)s" % locals()
> 'test - test - test'
>

I'm not sure about that last one being more common. I don't think I have
ever had a desire to pass more variables to the string format than the ones
I know I really want there.

If you've upgraded to Python 2.3, then another way to write this would be:

>>> "%(var)s - %(var)s - %(var)s" % dict(var="test")

--
Duncan Booth dun...@rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?

Anders J. Munch

unread,
Aug 20, 2003, 9:31:49 AM8/20/03
to
"Cliff Wells" <logi...@qwest.net> wrote:
> Here's another approach:
>
> >>> def fjoin(sep, s, n):
> ... return sep.join(["%s"] * n) % ((s,) * n)

Better this:

def fjoin(sep, s, n):
return sep.join((s,)*n)

Works even if sep contains a % character.

>>> fjoin(' % ', "test", 3)
'test % test % test'

- Anders


Jeff Epler

unread,
Aug 19, 2003, 5:30:23 PM8/19/03
to

Skip Montanaro

unread,
Aug 19, 2003, 5:45:09 PM8/19/03
to

Skip> To force the * operation to be evaluated first you need to add some parens:

Skip> ("%s - %s - %s" % (("test",))*3)

Ack! Should have been what I entered at the >>> prompt:

"%s - %s - %s" % (("test",)*3)

(Stupid copy-n-paste!)

Skip

Fredrik Lundh

unread,
Aug 19, 2003, 5:57:36 PM8/19/03
to
Michael C. Neel wrote:

> Hmmm, can't say I like what this implies. In one case % is shorthand
> for a sprintf function, the other it's a mathematical expression at the
> same level of precedence of * and /. But the sprintf version is
> "granted" the precedence of the mathematical version? What's the
> logic behind that?

it's the same "%", and the same "*".

consider the expression "a % b * c".

using your logic, do you really expect the evaluation order to vary
depending on the actual types of the objects a, b, and c?

</F>


Oren Tirosh

unread,
Aug 19, 2003, 5:37:09 PM8/19/03
to
On Tue, Aug 19, 2003 at 05:25:19PM -0400, Michael C. Neel wrote:
> I've got this string in which I need to sub in the same word several
> times; i.e:
>
> >>> "%s - %s - %s" % ("test","test","test")
> 'test - test - test'
> >>>
>
> But I want to use the * to make life easier to read, so I tried:
>
> >>> ("test",)*3
> ('test', 'test', 'test')
> >>> "%s - %s - %s" % ("test",)*3
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> TypeError: not enough arguments for format string
> >>>

It's just an issie of evaluation order. Try adding parentheses:

>>> "%s - %s - %s" % (("test",)*3)

'test - test - test'

Oren

0 new messages