我一直在使用n = int(n)
将float
转换为int
。
最近,我遇到了另一种做同样事情的方法:
n = n // 1
哪种方式最有效,为什么?
答案 0 :(得分:17)
使用timeit
:
$ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'int(n)'
10000000 loops, best of 3: 0.234 usec per loop
$ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'n // 1'
10000000 loops, best of 3: 0.218 usec per loop
因此,地板划分只是一个较小的速度。请注意,这些值非常关闭,我不得不提高循环重复次数来消除我机器上的随机影响。即使计数如此之高,您也需要重复几次实验才能看到数字仍然变化多少以及大多数的时间更快。
这是合乎逻辑的,因为int()
需要全局查找和函数调用(因此推送和弹出状态):
>>> import dis
>>> def use_int(n):
... return int(n)
...
>>> def use_floordiv(n):
... return n // 1
...
>>> dis.dis(use_int)
2 0 LOAD_GLOBAL 0 (int)
3 LOAD_FAST 0 (n)
6 CALL_FUNCTION 1
9 RETURN_VALUE
>>> dis.dis(use_floordiv)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (1)
6 BINARY_FLOOR_DIVIDE
7 RETURN_VALUE
LOAD_GLOBAL
和CALL_FUNCTION
操作码比LOAD_CONST
和BINARY_FLOOR_DIVIDE
操作码慢; LOAD_CONST
是一个简单的数组查找,LOAD_GLOBAL
需要进行字典查找。
将int()
绑定到本地名称会产生一些小的差异,再次给它带来优势(因为它必须做的工作少于// 1
楼层划分):
$ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'int(n)'
10000000 loops, best of 3: 0.233 usec per loop
$ bin/python -mtimeit -n10000000 -s 'n = 1.345; int_=int' 'int_(n)'
10000000 loops, best of 3: 0.195 usec per loop
$ bin/python -mtimeit -n10000000 -s 'n = 1.345' 'n // 1'
10000000 loops, best of 3: 0.225 usec per loop
同样,你需要运行1000万次循环来持续观察差异。
尽管如此,int(n)
更明确,除非你在一个时间关键的循环中这样做,int(n)
在n // 1
的可读性上赢得它。时间差异太小,无法让必须计算出// 1
所做的值得的认知成本。
答案 1 :(得分:3)
虽然Martijn Pieters回答了你关于什么更快以及如何测试它的问题,但我觉得速度对于这么小的操作并不重要。 Inbar Rose说,我会使用int()来提高可读性。通常在处理某些事情时,这种小的可读性更为重要;虽然,一个常见的等式可能是一个例外。
答案 2 :(得分:2)
实际上,int
似乎比分裂更快。缓慢的部分是在全球范围内寻找功能。
如果我们避开它,这是我的数字:
$ python -mtimeit -s 'i=int; a=123.456' 'i(a)'
10000000 loops, best of 3: 0.122 usec per loop
$ python -mtimeit -s 'i=int; a=123.456' 'a//1'
10000000 loops, best of 3: 0.145 usec per loop
答案 3 :(得分:1)
请注意,您没有使用楼层除法运算符从float转换为int 。此操作的结果仍然是浮点数。在Python 2.7.5(CPython)中,n=n//1
完全相同:
n.__floordiv__(1)
基本上是相同的:
n.__divmod__(1)[0]
两个函数都返回float而不是int。在CPython __divmod__
函数内部,分母和分子必须从PyObject转换为double。因此,在这种情况下,使用floor
函数而不是//
运算符会更快,因为只需要进行一次转换。
from cmath import floor
n=floor(n)
如果您真的想将float转换为整数,我认为没有办法打败int(n)性能。
答案 4 :(得分:0)
只是进行一项有趣的统计测试 - 将时间测试更改为您喜欢的任何内容:
import timeit
from scipy import mean, std, stats, sqrt
# Parameters:
reps = 100000
dups = 50
signif = 0.01
timeit_setup1 = 'i=int; a=123.456'
timeit_test1 = 'i(a)'
timeit_setup2 = 'i=int; a=123.456'
timeit_test2 = 'a//1'
#Some vars
t1_data = []
t2_data = []
frmt = '{:.3f}'
testformat = '{:<'+ str(max([len(timeit_test1), len(timeit_test2)]))+ '}'
def reportdata(mylist):
string = 'mean = ' + frmt.format(mean(mylist)) + ' seconds, st.dev. = ' + \
frmt.format(std(mylist))
return string
for i in range(dups):
t1_data.append(timeit.timeit(timeit_test1, setup = timeit_setup1,
number = reps))
t2_data.append(timeit.timeit(timeit_test2, setup = timeit_setup2,
number = reps))
print testformat.format(timeit_test1) + ':', reportdata(t1_data)
print testformat.format(timeit_test2) + ':', reportdata(t2_data)
ttest = stats.ttest_ind(t1_data, t2_data)
print 't-test: the t value is ' + frmt.format(float(ttest[0])) + \
' and the p-value is ' + frmt.format(float(ttest[1]))
isit = ''
if float(ttest[1]) > signif:
isit = "not "
print 'The difference of ' + \
'{:.2%}'.format(abs((mean(t1_data)-mean(t2_data))/mean(t1_data))) + \
' +/- ' + \
'{:.2%}'.format(3*sqrt((std(t1_data)**2 + std(t2_data)**2)/dups)) + \
' is ' + isit + 'significative.'
答案 5 :(得分:0)
使用float.__trunc__()
比builtins.int()
快30%
@MartijnPieters绑定builtins.int
的技巧确实很有趣,它使我想起An Optimization Anecdote。但是,调用builtins.int
并不是最有效的方法。
让我们看看这个:
python -m timeit -n10000000 -s "n = 1.345" "int(n)"
10000000 loops, best of 5: 48.5 nsec per loop
python -m timeit -n10000000 -s "n = 1.345" "n.__trunc__()"
10000000 loops, best of 5: 33.1 nsec per loop
那是30%的收益!这是怎么回事?
事实证明,所有builtints.int
所做的都是调用以下method-chains:
1.345.__int__
,则返回1.345.__int__()
否则:1.345.__index__
,则返回1.345.__index__()
否则:1.345.__trunc__
,则返回1.345.__trunc__()
1.345.__int__
未定义 1 -1.345.__index__
均未定义。因此,直接调用1.345.__trunc__()
可以使我们跳过所有不必要的方法调用-这相对昂贵。
绑定技巧如何? float.__trunc__
本质上只是一个实例方法,我们可以将1.345
作为self
参数传递。
python -m timeit -n10000000 -s "n = 1.345; f=int" "f(n)"
10000000 loops, best of 5: 43 nsec per loop
python -m timeit -n10000000 -s "n = 1.345; f=float.__trunc__" "f(n)"
10000000 loops, best of 5: 27.4 nsec per loop
这两种方法均根据 预期 2 进行了改进,并且它们保持大致相同的比率!
1 我对此并不完全确定-如果有人另外知道,请纠正我。
2 这让我感到惊讶,因为我印象中float.__trunc__
在实例创建期间绑定到1.345
。如果有人愿意向我解释一下,那就太好了。
还有一种方法builtins.float.__floor__
在文档中未提及-比builtins.int
快但比buitlins.float.__trunc__
慢。
python -m timeit -n10000000 -s "n = 1.345; f=float.__floor__" "f(n)"
10000000 loops, best of 5: 32.4 nsec per loop
在负浮点数和正浮点数上似乎产生相同的结果。如果有人可以解释一下这在其他方法中的适用性,那将很棒。