Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7676303
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 31, 20262026-05-31T17:10:10+00:00 2026-05-31T17:10:10+00:00

One of the changes between python 2 and 3 is that the latter delegates

  • 0

One of the changes between python 2 and 3 is that the latter delegates to x.__round__([n]) the operation round(x, n). In python 2, for my classes implementing __round__ and __float__, when I call round(x), x.__float__ is called.

How can I know that round(x) (and not float(x)) was called to reroute the call appropriate in python 2 and obtain a python 3 like behaviour.

thanks

Update: I came up with an ugly hack. I am sure that:

  • it can be improved.
  • it will not always work.
  • The ndigits parameter is not handled in python 2.
  • it should not be used in production.

but it was interesting to build it anyway. Thanks for all the clarifications.

import dis
import sys
import inspect
import functools

#'CALL_FUNCTION', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW'
HUNGRY = (131, 140, 141, 142)

if sys.version < '3':
    def is_round(frame):
        """Disassemble a code object."""
        co = frame.f_code
        lasti = frame.f_lasti
        code = co.co_code
        i, n = 0, len(code)
        extended_arg = 0
        free = None
        codes = list()
        while i < n:
            c = code[i]
            op = ord(c)
            tmp = [op, ]
            i += 1
            if op >= dis.HAVE_ARGUMENT:
                oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg
                extended_arg = 0
                i += 2
                if op == dis.EXTENDED_ARG:
                    extended_arg = oparg * long(65536)
                tmp.append(oparg)
                if op in dis.hasconst:
                    tmp.append(repr(co.co_consts[oparg]))
                elif op in dis.hasname:
                    tmp.append(co.co_names[oparg])
                elif op in dis.hasjrel:
                    tmp.append(repr(i + oparg)),
                elif op in dis.haslocal:
                    tmp.append(co.co_varnames[oparg])
                elif op in dis.hascompare:
                    tmp.append(dis.cmp_op[oparg])
                elif op in dis.hasfree:
                    if free is None:
                        free = co.co_cellvars + co.co_freevars
                    tmp.append(free[oparg])
                else:
                    tmp.append(None)
            else:
                tmp.append(None)
                tmp.append(None)

            codes.append(tmp)
            if i > lasti:
                break

        pending = 1
        for (opcode, arguments, param) in reversed(codes):
            pending -= 1
            if opcode in HUNGRY:
                pending += arguments + 1
            if not pending:
                seen = dict(frame.f_builtins)
                seen.update(frame.f_globals)
                seen.update(frame.f_locals)
                while param in seen:
                    param = seen[param]
                return param == round

    def round_check(func):
        @functools.wraps(func)
        def wrapped(self):
            if is_round(inspect.currentframe().f_back):
                return self.__round__()
            return func(self)
        return wrapped

else:

    def round_check(func):
        return func

class X():

    @round_check
    def __float__(self):
        return 1.0

    def __round__(self, ndigits=0):
        return 2.0

x = X()

r = round
f = float

assert round(x) == 2.0
assert float(x) == 1.0

assert r(x) == 2.0
assert f(x) == 1.0

assert round(float(x)) == 1.0
assert float(round(x)) == 2.0
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-31T17:10:11+00:00Added an answer on May 31, 2026 at 5:10 pm

    You could always redefine round to try __round__ first. Unfortunately this isn’t a __future__ import, so I don’t think there’s much else you can do.

    >>> class X(object):
    ...     def __round__(self, n=0): return 1.
    ...     def __float__(self): return 2.
    ... 
    >>> x = X()
    >>> round(x)
    2.0
    >>> float(x)
    2.0
    >>> old_round = round
    >>> def round(x, n=0):
    ...     try:
    ...             return x.__round__(n)
    ...     except AttributeError:
    ...             return old_round(x)
    ... 
    >>> 
    >>> round(x)
    1.0
    >>> float(x)
    2.0
    >>> 
    

    Note that this is at least a documented change:

    The round() function rounding strategy and return type have changed.
    Exact halfway cases are now rounded to the nearest even result instead
    of away from zero. (For example, round(2.5) now returns 2 rather than
    3.) round(x[, n])() now delegates to x.__round__([n]) instead of always returning a float. It generally returns an integer when called
    with a single argument and a value of the same type as x when called
    with two arguments.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Does anybody know of a diff-like tool that can show me the changes between
I was studying the difference between lists and tuples (in Python). An obvious one
In MS Word, there's a nice feature called track changes that allows the program
I have made some changes in one perforce client, but haven't submitted them. I
Given the case I made two independent changes in one file: eg. added a
I suppose it allows for moving changes from one branch to the next but
One of my coworkers is having a problem pushing changes from git on his
Our website has one section which often changes to match the user. (examples are
How can one use Triggers for Logging History of database changes in MySQL? If
I am merging a CVS branch and one of the larger changes is the

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.