2

[Python 3.1]

I'm following up on this answer:

class prettyfloat(float):
  def __repr__(self):
    return "%0.2f" % self

I know I need to keep track of my float literals (i.e., replace 3.0 with prettyfloat(3.0), etc.), and that's fine.

But whenever I do any calculations, prettyfloat objects get converted into float.

What's the easiest way to fix it?

EDIT:

I need exactly two decimal digits; and I need it across the whole code, including where I print a dictionary with float values inside. That makes any formatting functions hard to use.

I can't use Decimal global setting, since I want computations to be at full precision (just printing at 2 decimal points).

@Glenn Maynard: I agree I shouldn't override __repr__; if anything, it would be just __str__. But it's a moot point because of the following point.

@Glenn Maynard and @singularity: I won't subclass float, since I agree it will look very ugly in the end.

I will stop trying to be clever, and just call a function everywhere a float is being printed. Though I am really sad that I can't override __str__ in the builtin class float.

Thank you!

Community
  • 1
  • 1
max
  • 49,282
  • 56
  • 208
  • 355
  • The easiest way to format nicely may be to use `decimal.Decimal`. What's wrong with that? – S.Lott Nov 03 '10 at 23:56
  • I've yet to see a case where subclassing a core type like `float` is the right thing to do. – Glenn Maynard Nov 04 '10 at 00:02
  • @S.Lott I need exactly two decimal digits; and I need it across the whole code, including where I print a dictionary with float values inside. @Glenn Maynard I don't have much hope, either :( Just thought maybe there's a clean way to make it work. – max Nov 04 '10 at 00:02
  • 3
    Having the `repr` of a class round the value is wrong behavior and may break things in strange and subtle ways, because Python code is supposed to be able to depend on `eval(repr(1.12345))` being a round-trip operation. `repr` is the wrong place to be handling formatting. – Glenn Maynard Nov 04 '10 at 00:18
  • Please **update** your question to include all the facts. Please don't add comments to a question which you own. Please **update** the question. – S.Lott Nov 04 '10 at 00:36

3 Answers3

5

I had a look at the answer you followed up on, and I think you're confusing data and its representation.

@Robert Rossney suggested to subclass float so you could map() an iterable of standard, non-adulterated floats into prettyfloats for display purposes:

# Perform all our computations using standard floats.
results = compute_huge_numbers(42)
# Switch to prettyfloats for printing.
print(map(prettyfloat, results))

In other words, you were not supposed to (and you shouldn't) use prettyfloat as a replacement for float everywhere in your code.

Of course, inheriting from float to solve that problem is overkill, since it's a representation problem and not a data problem. A simple function would be enough:

def prettyfloat(number):
    return "%0.2f" % number  # Works the same.

Now, if it's not about representation after all, and what you actually want to achieve is fixed-point computations limited to two decimal places everywhere in your code, that's another story entirely.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
3

that because prettyfloat (op) prettyfloat don't return a prettyfloat

example:

>>> prettyfloat(0.6)
0.60  # type prettyfloat
>>> prettyfloat(0.6) + prettyfloat(4.4)
5.0  # type float

solution if you don't want to cast every operation result manually to prettyfloat and if you still want to use prettyfloat is to override all operators.

example with operator __add__ (which is ugly)

class prettyfloat(float):
  def __repr__(self):
      return "%0.2f" % self
  def __add__(self, other):
      return prettyfloat(float(self) + other)

>>> prettyfloat(0.6) + prettyfloat(4.4)
5.00

by doing this i think you will have also to change the name from prettyfloat to uglyfloat :) , Hope this will help

mouad
  • 67,571
  • 18
  • 114
  • 106
  • 1
    As an example of the sort of gotcha you'll hit with this sort of thing, the results of `prettyfloat(1.12345) + 1.12345` and `1.12345 + prettyfloat(1.12345)` are very different. – Glenn Maynard Nov 04 '10 at 00:21
  • yeap that why he shouldn't do it, he can make it more uglier by doing some type check each time to solve this problem :) – mouad Nov 04 '10 at 00:32
1

Use decimal. This is what it's for.

>>> import decimal
>>> decimal.getcontext().prec = 2
>>> one = decimal.Decimal("1.0")
>>> three = decimal.Decimal("3.0")
>>> one / three
Decimal('0.33')

...unless you actually want to work with full-precision floats everywhere in your code but print them rounded to two decimal places. In that case, you need to rewrite your printing logic.

Katriel
  • 120,462
  • 19
  • 136
  • 170
  • 1
    This is changing the precision of the value, not just how it's displayed, which is very different. I also have to seriously question the design competence of a module with global configuration. If you use a library (or several) which uses this module behind the scenes, it's hard to imagine the number of obscure and hard-to-fix ways you could be bitten by context configuration changes. – Glenn Maynard Nov 04 '10 at 00:28
  • (I wish they'd fix the site bug that lets people edit answers without them showing up as edited.) – Glenn Maynard Nov 04 '10 at 00:29
  • @Glenn: I believe there's a short window after posting where you can edit without marking your post as edited. You are indeed correct, of course =). – Katriel Nov 04 '10 at 08:24