'complex' function with string argument.

J

Jayanth Koushik

Hi everyone!

This is regarding the inbuilt 'complex' function. The python docs say:
"Note: When converting from a string, the string must not contain whitespace around the central + or - operator. For example, complex('1+2j') is fine,but complex('1 + 2j') raises ValueError."

Why is this so? Why can't spaces be allowed everywhere and simply ignored? I went through the source and it did not seem like this was an internal requirement, so it seems like a design choice. Is there any reason why spaces in the middle would be a problem?

Jayanth Koushik
 
M

Mark H Harris

On 3/15/14 11:26 AM, Jayanth Koushik wrote:

This is a very interesting philosophical question, one which I am
surprised no one has answered; although, I think the reason for that
might be entirely obvious.

You actually answered your own question, as you were asking it. If the
doc says "whatever you do, don't push the purple button," well, leave
the purple button alone. :) (I don't know, push it if you want)

If you monitor the PEP process, or have ever taken part in python-ideas,
or python-dev (either directly, or just lurking) you will notice that
python is developed through a very interesting active committee process
(that is really something phenomenal; cool community).

How should one spell a complex number? Should we use i or j ? Should the
imaginary part be set off somehow? Should literals be parsed
differently (or consistently) with correctly formed strings? Who knows,
beats me.

consider:I don't know... you tell me.
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
complex('3 +2j')
ValueError: complex() arg is a malformed string
Again, beats me. I just don't know.

But I do know that the spelling book says, Do it this way:
complex('3+2j').

Seems simple enough.


Philosophically, I tend to think about it this way. A complex number is
like any other number. I would not form a PI string like this> ' 3 .14 1
5 9265 3 . . .' I would rather see it formed like so, '3.1415926535'

This '3 + 2j' is not a number, its an algebraic sum.

This '3+2j' is a complex number. Ok, maybe not, but its closer to
what we expect (I'm sorry, but I like i instead of j )

Also, philosophically, C ignores white space; python does not.

I agree with this now; before I did not. White space is just as much a
part of how interpretation occurs, within symbol processing/parsing.
Some choices are arbitrary, some are community concurrence (PEPs), some
are philosophical, and most are just convenient intuition.

My Greek professor used to say, "there is no 'why' in Greek".

Python is similar.

Cheers
 
C

Chris Angelico

You actually answered your own question, as you were asking it. If the doc
says "whatever you do, don't push the purple button," well, leave the purple
button alone. :) (I don't know, push it if you want)
https://www.wizards.com/magic/magazine/article.aspx?x=mtg/daily/mm/69

If you monitor the PEP process, or have ever taken part in python-ideas, or
python-dev (either directly, or just lurking) you will notice that python is
developed through a very interesting active committee process (that is
really something phenomenal; cool community).

Not really a committee, more of a champion-and-bikeshedders approach -
often with more than one level of champion, as when a PEP has an
author (the first champion) and either the BDFL or his delegate (the
second champion, whose role is usually just to say yay or nay). It's a
curious process, but one that works fairly well.
How should one spell a complex number? Should we use i or j ? Should the
imaginary part be set off somehow? Should literals be parsed differently
(or consistently) with correctly formed strings? Who knows, beats me.

consider:
I don't know... you tell me.

That's for the sake of parsing clarity. (Incidentally, the call to
complex() is redundant in each case.) Everything in Python consists of
tokens - those tokens, in your examples, are:

"complex", "(", whitespace, "3", whitespace, "+", whitespace, "2",
whitespace, "j", ")", end of line

and

"complex", "(", whitespace, "3", whitespace, "+", "2j", whitespace,
")", end of line

In the first case, the parser then has two symbol-type tokens ("2" and
"j") separated by whitespace, with no operator. That's a problem. Did
you mean "2+j", or "2==j", etc? Since j is perfectly natural as a
name, it's going to be interpreted that way.

In the second case, that translates into a perfectly valid parse tree,
because "2j" is an imaginary literal.
"Module(body=[Expr(value=Call(func=Name(id='complex', ctx=Load()),
args=[BinOp(left=Num(n=3), op=Add(), right=Num(n=2j))], keywords=[],
starargs=None, kwargs=None))])"

The sole argument to complex() is an expression which sums the integer
3 and the imaginary 2j, which results in the complex (3+2j), which
complex() looks at and returns unchanged. And that's what you see.
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
complex('3 +2j')
ValueError: complex() arg is a malformed string

Again, beats me. I just don't know.

And now what you're looking at is the construction of a complex from a
string. Now, instead of going by the rules of the Python lexer, it
goes by the rules of the complex constructor. You can't use extra
spaces there. You could, of course, write your own function that
parses whatever format you like (including the use of i instead of j),
or you can use ast.literal_eval to parse a string based on Python's
lexing, but with complex(str) you follow the rules of complex(str).
Philosophically, I tend to think about it this way. A complex number is like
any other number. I would not form a PI string like this> ' 3 .14 1 5 9265 3
. . .' I would rather see it formed like so, '3.1415926535'
Right.

This '3 + 2j' is not a number, its an algebraic sum.

This '3+2j' is a complex number. Ok, maybe not, but its closer to what we
expect (I'm sorry, but I like i instead of j )

Hmm. That's a pretty tricky distinction. In Python source code, those
two are identical, and they're both rendered as a sum. (Lexically. The
CPython optimizer, and presumably other Pythons' optimizers, will
notice at compile time that you're adding two literals, and store the
sum. But as you see from the AST above, it's the sum of two values.)
It's actually not possible, as far as I know, to truly represent a
complex number; all you can do is represent the sum of a real part and
an imaginary part.
Also, philosophically, C ignores white space; python does not.

That's not really anything to do with it. The two languages'
approaches to whitespace inside expressions are identical, save that
Python will only allow newlines if the expression is "clearly
unfinished", eg if it has unbalanced open parens. Horizontal
whitespace is fine in both languages. (Of course, C doesn't even
_have_ a complex literal notation, so the distinction is slightly
moot.)

ChrisA
 
M

Mark Dickinson

Jayanth Koushik said:
"Note: When converting from a string, the string must not contain whitespace
around the central + or - operator. For example, complex('1+2j') is fine, but
complex('1 + 2j') raises ValueError."

Why is this so?

See http://bugs.python.org/issue9574 for a bit more context. To begin with,
it's not at all clear what *should* be allowed. If "1 + 2j" is valid, what
about "+ 2j"? How about "+ 2"? What about things like "+1.0 + -2.3j"?
Ultimately, I closed that issue because the proposed change seemed like
unnecessary code churn, and there wasn't a clear consensus that the change was
desirable (or even what that change should be). If it ain't broke, etc.
 
M

Marko Rauhamaa

Chris Angelico said:

Well, Java 7 allows you to embed underscores freely in numeric literals.
Would be a nice addition to Python as well:

if unit == 'G':
count *= 1_000_000_000

vs:

if unit == 'G':
count *= 1000000000

Hmm. That's a pretty tricky distinction.

Is "-2.0" a literal?

What's the outcome of

-2.0.__str__()


Marko
 
M

Mark H Harris

"Module(body=[Expr(value=Call(func=Name(id='complex', ctx=Load()),
args=[BinOp(left=Num(n=3), op=Add(), right=Num(n=2j))], keywords=[],
starargs=None, kwargs=None))])"

The sole argument to complex() is an expression which sums the integer
3 and the imaginary 2j, which results in the complex (3+2j), which
complex() looks at and returns unchanged. And that's what you see.

~very nice.

Ok, going along with Mark's comment about this bug report:

This string '3 +2j' should probably be ok from the complex() string
constructor standpoint, right?

I mean, there might be more than one constructor for string, mighten-it?

marcus
 
I

Ian Kelly

Is "-2.0" a literal?

What's the outcome of

-2.0.__str__()

No. The compiler will try to optimize it into a single constant if it
can, but it has to be done in accordance with the order of operations.
In that example, the __str__ method is called before the unary - is
applied, resulting in an error.
 
C

Chris Angelico

Is "-2.0" a literal?

What's the outcome of

-2.0.__str__()

If you mean (-2.0).__str__(), then it returns '-2.0', but that proves
nothing. Lots of objects have a str() which isn't a literal. Closer to
what you're talking about would be repr(), but even then, it doesn't
prove that something's a literal. The easiest way to tell is probably
ast.parse():
'Module(body=[Expr(value=UnaryOp(op=USub(), operand=Num(n=2.0)))])'

It's an expression consisting of unary minus and the float literal 2.0.

ChrisA
 
M

Marko Rauhamaa

Chris Angelico said:
If you mean (-2.0).__str__(), then it returns '-2.0', but that proves
nothing.

The point is, you don't need to "philosophize" about complex literals
when even negative numbers don't have literals in Python.


Marko
 
C

Chris Angelico

The point is, you don't need to "philosophize" about complex literals
when even negative numbers don't have literals in Python.

Ah! I get you.

The difference between literals and constants is one that almost never
matters, though. Python may not have a syntax for negative or complex
literals, but it does have notations for various sorts of constants,
which function the same way. (Literals are by definition constants.)
So Python may not have a convenient notation for "number of seconds in
a week" (unless you work with DNS and find the bare integer 604800
convenient), but you can write 7*24*60*60 and it's just as good in a
function:
'Module(body=[Expr(value=BinOp(left=BinOp(left=BinOp(left=Num(n=7),
op=Mult(), right=Num(n=24)), op=Mult(), right=Num(n=60)), op=Mult(),
right=Num(n=60)))])'return x + 7*24*60*60
2 0 LOAD_FAST 0 (x)
3 LOAD_CONST 6 (604800)
6 BINARY_ADD
7 RETURN_VALUE

There's absolutely no run-time cost to writing it out, and you get to
be flexible with whitespace and such, which you can't do with
literals.

ChrisA
 
T

Terry Reedy

See http://bugs.python.org/issue9574 for a bit more context. To begin with,
it's not at all clear what *should* be allowed. If "1 + 2j" is valid, what
about "+ 2j"? How about "+ 2"? What about things like "+1.0 + -2.3j"?
Ultimately, I closed that issue because the proposed change seemed like
unnecessary code churn, and there wasn't a clear consensus that the change was
desirable (or even what that change should be). If it ain't broke, etc.

I was not nosy on that issue, but I agree with 'as is', along with no
space in Franction("1/2"). Mathematicians genearally write both without
spaces.
 
S

Skip Montanaro

Mathematicians genearally write both without spaces.

Mathematicians also have a tendency to use single letter variables and
often escape into non-ASCII character sets as well. <wink>

Perhaps it's worth pointing out that pylint complains about most/many
infix operations if you don't surround the operator with white space.
And, complex expressions work just fine with white space around the
operator:
(1+2j)

Personally, I'm agnostic to the proposed change. I don't use complex
numbers in my work, and I suspect that creating complex numbers from
strings is an extremely small corner case.

Skip
 
C

Chris Angelico

Perhaps it's worth pointing out that pylint complains about most/many
infix operations if you don't surround the operator with white space.

IMO that's excessive. Not every infix operator needs whitespace.

ChrisA
 
S

Skip Montanaro

IMO that's excessive. Not every infix operator needs whitespace.

I wasn't suggesting it was the only way things could be done. Just
pointing out that there is enough common practice out there to suggest
that white space around infix operators is often the preferred way of
doing things. Quoting from PEP 8:

If operators with different priorities are used, consider adding
whitespace around the operators with the lowest priority(ies). Use
your own judgment; however, never use more than one space, and always
have the same amount of whitespace on both sides of a binary operator.

Yes:

i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

....

My point is that accommodating white space around the + or - sign
isn't altogether unreasonable.

Maybe PEP 8 needs to be tweaked with an example of good complex
literal practice?

Skip
 
S

Steven D'Aprano

The point is, you don't need to "philosophize" about complex literals
when even negative numbers don't have literals in Python.

But Python *does* have complex (well, imaginary to be precise) literals.
The j suffix to a number makes it complex:

py> type(2j)
<class 'complex'>


Complex numbers with both a real and imaginary component are not treated
as literals:

py> import ast
py> ast.dump(ast.parse("2j"))
'Module(body=[Expr(value=Num(n=2j))])'
py> ast.dump(ast.parse("1+2j"))
'Module(body=[Expr(value=BinOp(left=Num(n=1), op=Add(),
right=Num(n=2j)))])'


and in recent versions, the peephole optimizer optimizes them:

py> from dis import dis
py> dis("1+2j")
1 0 LOAD_CONST 2 ((1+2j))
3 RETURN_VALUE
 
S

Steven D'Aprano

How should one spell a complex number? Should we use i or j ? Should the
imaginary part be set off somehow? Should literals be parsed
differently (or consistently) with correctly formed strings? Who knows,
beats me.

With respect, that's just because you would make a lousy language
designer :) The answer to most of those questions should be pretty
obvious, with perhaps just one that isn't clear.

"How should one spell a complex number?" There is perfectly good syntax
for complex numbers used by mathematicians and engineers for over a
century. There is no need to invent something different just for the sake
of being different:

Yes: 2+3i or 2+3j
No: @2|3?


"Should we use i or j ?" There are good reasons for both i and j. This
one comes down to personal preference.


"Should the imaginary part be set off somehow?" What do you mean "set
off"? Why do you want to? Since the imaginary part can appear on its own:

z = 3j

we cannot make "setting off" compulsory. Once you have syntax for complex
numbers with an imaginary component, everything else Just Works with no
additional effort:

z = 2 + 3j # an expression adding 2 to 3j
z = 5*3j # an expression multiplying 5 by 3j

There's no need for dedicated syntax for complex numbers beyond the
simple no-real-part case. You get everything else for free from basic
arithmetic expressions, so there actually isn't need to parse complex
literals beyond the j suffix.


"Should literals be parsed differently (or consistently) with correctly
formed strings?" Once you decide that complex literals should be formed
from only the imaginary part, 3j, parsing literals is simple. So is
passing strings: you have something that looks like a float, with a j
suffix. Obviously they should parse the same.

assert 1.234j == complex('1.234j')

Problem solved.

Well, not quite -- it would be rather limiting if the complex constructor
only accepted complex numbers with an implicitly zero real part. We'd
like to accept anything that repr(z) can print. Since repr(z) prints
complex numbers with a + or - infix operator, the complex constructor
should accept the same inside strings.

How flexible should the complex constructor be? Should it bend over
backwards to accept any mathematical expression that includes a complex j
suffix, e.g. complex("2**3i")? I think not, since complex numbers don't
display like that. Our only obligation is to parse the strings that
complex.__repr__ can produce, not to parse any imaginable numeric
expression.

So at a minimum, complex should accept strings that look like

<float>j
<float>+<float>j
<float>-<float>j

For the same reason that float("2") works, we should also allow strings
that look like:

<float>

with no j suffix. Anything else, including spaces around the + and -
symbols, would be a bonus.

consider:
SyntaxError: invalid syntax

That's a syntax error for the same reason that:

x = 1 2

is a syntax error. Nothing to do with the + sign. It's the spaces between
the 2 and the j.

I don't know... you tell me.

In both cases, the call to complex is redundant. You've already created a
complex number, using syntax, then you pass it to the complex function
which just returns the same number.

Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
complex('3 +2j')
ValueError: complex() arg is a malformed string


Personally, I would like complex to accept spaces around the + or -, as
it already accepts leading and trailing spaces. But it's not a big deal.


[...]
Also, philosophically, C ignores white space; python does not.

C does not ignore whitespace.

forwhile

is not the same as

for while


The first is a valid identifier, the second is a syntax error. Oh
somebody please tell me it's not a valid C expression! *wink*
 
C

Chris Angelico

The first is a valid identifier, the second is a syntax error. Oh
somebody please tell me it's not a valid C expression! *wink*

It's not a valid C expression.

ChrisA
 
S

Steven D'Aprano

Am 15.03.14 17:26, schrieb Jayanth Koushik:

It's funny that you ask this question exactly now; because I'm currently
implementing a compiler for a small language that understands complex
numbers. As others have explained, the basic issue is the question how
to parse an expression like

1+2i*3

is it "complex(1+2i) times 3"

Putting my mathematician's hat on, I would say *absolutely not*.
or is it sum of 1 and product of complex 2i and 3?

This one. Multiplication has higher precedence than addition, so 1+2i*3
has to be evaluated as 1+(2i*3).
 
M

Mark H Harris

With respect, that's just because you would make a lousy language
designer :)

Ouch ;-)
"How should one spell a complex number?" There is perfectly good syntax
for complex numbers used by mathematicians and engineers for over a
century. There is no need to invent something different just for the sake
of being different:
Yes: 2+3i or 2+3j

Yes. my Q was rhetorical only. And for the sake of discussion,
particularly on the part of the OP, to think about what one expects
based on experience and common sense, as a mathematician. (that doesn't
mean it will be easy for the lexical parser.
"Should we use i or j ?" There are good reasons for both i and j. This
one comes down to personal preference.

no, not really. nobody writes, e^(jPI) + 1 = 0

Euler wants to see e^(iPI) + 1 = 0 ;-)
"Should the imaginary part be set off somehow?" What do you mean "set
off"? Why do you want to? Since the imaginary part can appear on its own:

simply, is 2j a literal complex part? ...yes 2 j ...no

In other words, there must NEVER be a space between the 2 and j.
This is debatable: 3 +2j although, I want to see 3+2j

z = 2 + 3j # an expression adding 2 to 3j
z = 5*3j # an expression multiplying 5 by 3j

all good, well until its not
How flexible should the complex constructor be? Should it bend over
backwards to accept any mathematical expression that includes a complex j
suffix, e.g. complex("2**3i")? I think not,

I think not either. But, it is possible to have *many* constructors/

But, really, how often is this a problem? like almost never!
In both cases, the call to complex is redundant. You've already created a
complex number, using syntax, then you pass it to the complex function
which just returns the same number.

Of course. I was merely pointing out that the constructor for
'literals' (whatever you guys mean by that) is 'different' than the
consistency with the string constructor. As Chris pointed out these are
two markedly different animals; one is a valid parse syntax, the other
is a string constructor. But here is my point--- to the user the
difference comes down to semantics, which is not really true.

cf. below

This was said tongue in cheek... *both* languages inconsistently
observer (and ignore) white space! But, in general, white space is more
important to python, and less important to C.

I'm still hung up on whether I'm a lousy language designer. I guess
we'll know if people use my language (or not)! I suppose they might use
it even though its lousy; on the other hand, it might be too simple for
folks to be able to figure it out. :)

marcus
 

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top