1

I am trying to implement my own class for complex numbers, to better understand how classes work in python. I have been trying to replace the str magic method to print the complex number in the a+bi format.

def __str__(self):
    out="%i" % self.real
    if self.imaginary==0: return out
    if self.imaginary>=0: out+="+%ii" % self.imaginary
    else: out+="%ii" % self.imaginary
    return out

what I am interested in is the pythonic way of writing this unappealing block of code, if there is any to implement the fact that if imaginary part is negative, i should get a-bi and if imaginary part is 0 i should get a?

chepner
  • 497,756
  • 71
  • 530
  • 681
Blaine
  • 576
  • 9
  • 30

2 Answers2

1

If you are using Python 3.6+, use f-strings as follows:

def __str__(self):
    if self.imaginary == 0:
        return f'{self.real}'
    if self.real == 0:
        return f'{self.imaginary}i'

    return f'{self.real} {self.imaginary:+}i'

If you are using a version prior to Python 3.6, you should use format.

def __str__(self):
    if self.imaginary == 0: return '{}'.format(self.real)
    if self.real == 0: return '{}i'.format(self.imaginary)

    return '{} {:+}i'.format(self.real, self.imaginary)

I have also improved a bit the logic. Basically, when it has no imaginary part it is just returning the real part, if there is no real part, it is returning the imaginary part. When it has both imaginary and real part, it returns the complex number.

Notice that + specified after the two dots is the format. This format allows you to have the the sign of the imaginary part printed.

Examples

Assuming that your class is named CNumber

>>> x = CNumber(10, 1)
>>> str(x)
'10 +1i'
>>> x = CNumber(5, 0)
>>> str(x)
'5'
>>> x = CNumber(0, 3)
'3i'
>>> x = CNumber(1, -1)
'1 -1i'

If you want the following format a + bi

def __str__(self):
    if self.imaginary == 0: return f'{self.real}'
    if self.real == 0: return f'{self.imaginary}i'

    sign = '+' if self.imaginary > 0 else '-'

    return f'{self.real} {sign} {abs(self.imaginary)}i'
lmiguelvargasf
  • 63,191
  • 45
  • 217
  • 228
  • this works, and it prints a +bi, what if i wanted to print a + bi , ie the space between the sign and the imaginary part? – Blaine Sep 27 '19 at 17:04
  • 1
    also is the pre 3.6 code valid for all versions of python? – Blaine Sep 27 '19 at 17:06
  • 1
    The pre 3.6 code depends on `str.format` which was introduced in python 2.6. Also, python 2 is reaching end of life: https://pythonclock.org/. Be part of the transition! – SyntaxVoid Sep 27 '19 at 17:07
  • 1
    @Blaine [PEP8](https://www.python.org/dev/peps/pep-0008/) explicitly says that **Compound statements (multiple statements on the same line) are generally discouraged.** – norok2 Sep 27 '19 at 17:10
  • 1
    @norok2, I just updated my answer, and made my code follow PEP-8 – lmiguelvargasf Sep 27 '19 at 17:15
  • @imiguelvargasf what if i wanted to bit 0 + 1i? the str function prints 1.0i instead of 1i, is it possible to have integer printed if any part is purely integer? – Blaine Sep 28 '19 at 17:18
  • @Blaine, remove the condition `self.real == 0`. – lmiguelvargasf Sep 28 '19 at 17:38
  • @lmiguelvargasf ah bad wording, I meant what if my compenents were purely integer? How would i print them as integer? for example if my number was 1i , the given function would print 1.0i instead of 1i – Blaine Sep 28 '19 at 22:09
  • @Blaine, one way is to check for the type using the built-in `type`, then return what you want. – lmiguelvargasf Sep 28 '19 at 23:26
1

What about just going a bit more explicit:

class Complex(object):
    IM_TOKEN = 'i'

    def __init__(self, real_part=0, imag_part=0):
        self.real = real_part
        self.imag = imag_part

    def null_imag(self):
        return self.imag == 0

    def null_real(self):
        return self.real == 0

    def __str__(self):
        if self.null_imag():
            return str(self.real)
        elif self.null_real():
            return str(self.imag) + type(self).IM_TOKEN
        else:
            return '{}{:+}{}'.format(self.real, self.imag, type(self).IM_TOKEN)

and testing it:

import itertools

for r, i in itertools.product([1, -1, 0], [1, -1, 0]):
    print(f'real: {r}, imag: {i}, complex: {Complex(r, i)}')
real: 1, imag: 1, complex: 1+1i
real: 1, imag: -1, complex: 1-1i
real: 1, imag: 0, complex: 1
real: -1, imag: 1, complex: -1+1i
real: -1, imag: -1, complex: -1-1i
real: -1, imag: 0, complex: -1
real: 0, imag: 1, complex: 1i
real: 0, imag: -1, complex: -1i
real: 0, imag: 0, complex: 0

As a side note the == does not work well with float data.

norok2
  • 25,683
  • 4
  • 73
  • 99