python - python 2 和 3 中的 __float__ 和 __round__

标签 python python-3.x porting

Python 2 和 3 之间的变化之一是后者将操作 round(x, n) 委托(delegate)给了 x.__round__([n])。在 python 2 中,对于实现 __round____float__ 的类,当我调用 round(x) 时,x.__float__ > 被调用。

我如何知道调用了round(x)(而不是float(x))来重新路由Python 2中适当的调用并获得像这样的Python 3行为。

谢谢

更新:我想出了一个丑陋的技巧。我确信:

  • 还可以改进。
  • 它并不总是有效。
  • Python 2 中不处理 ndigits 参数。
  • 不应在生产中使用它。

但无论如何构建它还是很有趣的。感谢您的所有澄清。

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

最佳答案

您始终可以重新定义round来首先尝试__round__。不幸的是,这不是 __future__ 导入,所以我认为您没有什么可以做的。

>>> 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
>>> 

请注意,这至少是 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.

关于python - python 2 和 3 中的 __float__ 和 __round__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9751476/

相关文章:

python - 我如何判断 Python 的多处理模块是否正在使用我的所有内核进行计算?

python - 除了方法 python pyAIML

python - 如何检查嵌套列表树的所有元素是否相同?

python - 如何序列化 Keras 模型以与 Joblib 一起使用?

python - Pandas - 导入大小为 4GB 的 CSV 文件时出现内存错误

java - 移植库时首先要做的事情

python - 导入请求适用于 Windows shell 但不适用于 PyCharm

python - 将句子的字符串表示形式列表转换为词汇集

python - pandas 数据透视表,通过多列的差异创建表

operating-system - 将 Cbor 库移植到 Beaglebone 运行 Xinu