python 2和3之间的一个变化是后者委托x.__round__([n])
操作round(x, n)
。在python 2中,对于我的实现__round__
和__float__
的类,当我调用round(x)
时,会调用x.__float__
。
我怎么知道round(x)
(而不是float(x)
)被调用来重新路由python 2中适当的调用并获得类似python 3的行为。
感谢
更新:我想出了一个丑陋的黑客。我确信:
但无论如何构建它都很有趣。感谢所有的澄清。
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
答案 0 :(得分:3)
您可以随时重新定义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:
round()
函数舍入策略和返回类型已更改。 确切的中途情况现在四舍五入到最接近的偶数结果 远离零。 (例如,round(2.5)
现在返回2
而不是3
round(x[, n])()
。)x.__round__([n])
现在委托给x
而不是总是返回浮动。它通常在调用时返回一个整数 调用时使用单个参数和与{{1}}相同类型的值 有两个论点。
答案 1 :(得分:1)
在Python 2中,您无法覆盖round()
所做的事情。它没有委托到__float__
;它首先调用float()
(然后委托给__float__
),然后进行舍入。因此,知道__float__
是否从round()
调用是没有意义的,因为它将为您进行舍入。你不能委托它。
如果要在Python 2中实现自己的自定义舍入,则应实现执行自定义舍入的custom_round()
方法,并使用该方法代替round()
。
答案 2 :(得分:0)
我怎么知道round(x)(而不是float(x))被调用来重新路由python 2中适当的调用并获得类似python 3的行为。
你不需要。如果round(x)
调用了您的__float__
方法,则会使用float
的常规逻辑对返回的float
进行舍入。您无需在__float__
实施中考虑它;无论调用上下文如何,都应该返回相同的内容。其他一切都是呼唤环境的责任。
>>> class hax(object):
... def __float__(self): return 2.6
...
>>> round(hax())
3.0