New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Complex numbers with negative zero parts do not roundtrip properly #71550
Comments
Look: >>> complex('1-0j')
(1-0j)
>>> 1-0j
(1+0j) Yes, I understand what's going on, and it's probably wrong / too much to expect 1-0j to work properly, but I'd really like the complex from string constructor to be consistent with that. Even more because (of course): >>> import ast
>>> ast.literal_eval('1-0j')
(1+0j) |
This has been discussed in multiple issues before this one. Currently, The other important property here is that Strong -1 from me. |
Breaking the repr invariant would be bad. Agree with rejection. |
Funny, I thought the "repr invariant" was exactly the opposite thing, that _eval_ (or literal_eval) of repr should give back the starting object. And that's what I intended to preserve here. It's obviously broken now. Ok, alternate suggestion: can at least ast.literal_eval be fixed to work in the same way as complex constructor? Because it's not much of a "literal" otherwise... |
That's a good point; however the goal of the "repr invariant" is to be able to losslessly reproduce the object when possible. The fact that you have to use the complex constructor to do that is unfortunate, but is a consequence of the underlying problem. I suspect that literal_eval, on the other hand, should reproduce what the interpreter does, but since I think it already doesn't do that 100% (though I can't offhand remember what makes me think that) perhaps that is a possibility. I'll reopen the issue to discuss that. |
I think that's going to be awkward to achieve without making the behaviour of literal_eval significantly less obvious and more DWIMmy. And I'm not convinced that Of course, the "right" fix here is to change the complex repr entirely so that it looks like the compound object that it is rather than an eval-able expression: >>> repr(1+2j)
complex(1.0, 2.0) That would break backwards compatibility, but given the number of times complaints come up on this tracker, I'm beginning to think it might be worth it. |
Mark, I think it would be a great idea. It would be consistent with both "str is straightforward, repr is reproducible", and with the idea that (evalable) repr is almost always of the form [ On the other hand, I started to think deeply about the reason why we cannot make 1-0j do the right thing, and I'm not so sure anymore. People usually say it's because we don't have separate imaginary type, but we don't need it: all we need is a separate _real_ type, and we have it: float. When Python subtracts 0j from a float 1.0, there is no absolute imperative that it has to do it in the same way as subtracting 0j from a complex(1.0, 0.0). We _can_ make it so the result of the former is complex(1.0, -0.0), and result of the latter is complex(1.0, 0.0). Of course, now even the imaginary complex numbers couldn't be outputed simply, and -0 real part should become -0.0, but it might be worth it. I understand it's enormously complicated though, and I'd be satisfied with a "normal" repr. Or a literal_eval that really understand complex numbers' repr. ] |
I think we do. Consider the case of something like -0 + 1j (the |
Yes, but IMO that's a separate issue. And if complex analysis has taught me anything, it's that the sign of zero of .imag is much more important than the sign of zero of .real part (most elementary functions have branch cuts along real axis, where sign of .imag ensures continuity on both sides). Of course, having both would be even better, but having only this part is a good part of a good thing. However, as I said, I know it's complicated. How about giving a "conventional" repr to complex? As far as I see, it's really not hard to implement - the only problem is backwards compatibility. But that was a problem when parentheses were added, too, right? [ And there would be one more benefit: We could finally say goodbye to weird "names" (infj, nanj) in the repr. By analogy with float, this could just be complex('nan', '-inf') or whatever. ] For what it's worth, I'm not sure we should try too hard to preserve complex(repr(z)) being z given isinstance(z, complex). For example, Fraction and Decimal don't have this property (while it does kinda hold for str instead of repr, and it would continue to kinda hold for str here). Yes, I know Fraction and Decimal aren't builtins and complex is, but I really think it's only because of syntax support for j-based literals. |
And of course, the most important argument that preserving type(x)(repr(x))==x is futile: try it with x=False. (Yes, bools are numbers too.:) On the other hand, of course, literal_eval treats 'False' completely fine. |
Right, that isn't the invariant. Eval is the normal invariant, but not all classes *can* meet it, in which case if they can provide a repr that *can* be turned back into the value losslessly somehow, that's better than not doing so at all. Still, changing the repr would be the best way to meet the desired invariant, if we're willing to do that. This could be the first stdlib case of a non-string having a useful __str__ if we make the __str__ still return what the current __repr__ does. |
...but hopefully not the last. People are playing (with BDFL's blessing) with the idea of types having just (qual)name as str. By "first stdlib case" you mean "first builtin case", right? fractions.Fraction and decimal.Decimal are in stdlib. :-) Yes, not all classes can (nor should) support the strong "literal_eval being left-inverse of repr" invariant, but IMO builtin value-like (having a meaningful ==/hash infrastructure separate from identity) classes should. Numbers are surely in that camp. |
Ah, I haven't used Fractions and Decimal much, so I hadn't noticed. Those postdate complex, I think, but do follow the model Mark is suggesting. Seems like it would be reasonble to make complex do the same...the concern of course is backward compatibilty. |
Unfortunately, as http://bugs.python.org/issue23229#msg233965 shows, Guido is against changing complex.__repr__. Is there any chance someone could show this discussion to him, to show how it would help and try to change his mind? |
Good catch! I think that means we should close this issue. The current behaviour isn't actually wrong; it's just a compromise between lots of different concerns. |
Since I'm so happy with Fraction being fixed, I'll agree here. :-) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: