2025, Oct 27 15:00
Python CLI formatting bugs: why stringifying iterables adds commas and parentheses, and how to stop duplicate results
Fix Python console formatting bugs that print commas, parentheses, and duplicate results by using iterable handling, f-strings, and alignment for CLI output.
When you build console formatting around arithmetic operations, a tiny misstep can scramble the output. In this case, a scientific calculator prints stray commas and parentheses, and the final result appears twice. The root cause isn’t in math—it’s in how strings and iterables are handled during layout.
Reproducing the issue
The following snippet demonstrates the exact behavior: unexpected characters in the middle of the output and a duplicated result line.
class CalcPretty:
    
    def __init__(self, *vals):
        self.vals = vals
    def render(self, nums, op, outcome):
        w = max(len(str(x)) for x in (list(nums) + list(outcome))) + 2
        rows = []
        rows.append(f"{str(nums[0]).rjust(w)}")
        for ch in str(nums[1:]):
            rows.append(f"{op}{ch.rjust(w-1)}")
        dashes = '-' * w
        rows.append(dashes)
        rows.append(f"{str(outcome).rjust(w)}")
        return "\n".join(rows)
    def sumup(self, *args):
        total = sum(self.vals)
        s_total = str(total)
        print(self.render(self.vals, '+', s_total))
        return s_total
obj = CalcPretty(23, 2, 3)
print(obj.sumup())
What’s going wrong and why
The output goes off the rails in two places. First, the expression that lays out subsequent values converts a tuple slice to a string. Calling str(nums[1:]) produces the tuple’s literal representation, for example "(2, 3)", and iterating over that string yields each character in turn, including parentheses, the comma, and the space. That is exactly why “(”, “,”, and “)” show up on separate lines with the operator prefixed.
The duplicated total is caused by mixing responsibilities: the method both prints the formatted block and returns a value, and then that return value is printed again by the caller. Printing inside and outside leads to two lines with the result.
There’s also a design nuance. It’s cleaner to pass the numbers to the operation method rather than storing them on the instance. It makes the flow explicit and avoids stale state. Finally, layout code shouldn’t assume that an operator is a single character; using its actual length makes the alignment resilient.
Fixing the formatting and the flow
The corrected version formats only the actual numbers, accounts for the operator width, and returns the fully composed block so the caller decides when to print it. f-strings make the alignment both expressive and compact.
class CalcOps:
    def layout_block(self, seq: tuple[int|float, ...], op: str, out_str: str) -> str:
        opw = len(op)
        span = max(len(str(v)) for v in (list(seq) + [out_str])) + opw + 1
        lines = [f"{seq[0]:>{span}}"]
        for v in seq[1:]:
            lines.append(f"{op}{v:>{span - opw}}")
        lines.append('-' * span)
        lines.append(f"{out_str:>{span}}")
        return "\n".join(lines)
    def plus(self, *vals: int|float) -> str:
        res = str(sum(vals))
        return self.layout_block(vals, "+", res)
print(CalcOps().plus(23, 2, 3))
Output:
  23
+  2
+  3
----
  28
Why this matters for production code
Stringifying containers like tuples or lists and then iterating over the result is a subtle trap: you end up iterating characters, not values. In formatted console or log output, that can produce misleading symbols that look like logic errors higher up the stack. Separating data computation from presentation also pays off. Returning a formatted block and letting the caller print it prevents duplicate output and makes the code easier to test.
Takeaways
Iterate over actual values, not over stringified containers, if you want clean numeric layout. Compute alignment based on real widths, including the operator itself, so the formatting doesn’t break when the operator changes. Keep printing at the edges: produce a string in the operation method, and print it at the call site. These small habits eliminate noisy output and make arithmetic formatting in CLI tools predictable and maintainable.