Я использовал n = int(n)
для преобразования a float
в int
.
Недавно я встретил другой способ сделать то же самое:
n = n // 1
Каков наиболее эффективный способ и почему?
Я использовал n = int(n)
для преобразования a float
в int
.
Недавно я встретил другой способ сделать то же самое:
n = n // 1
Каков наиболее эффективный способ и почему?
Протестируйте его с помощью 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
Опять же, вам нужно запустить это с помощью 10 миллионов циклов, чтобы последовательно видеть различия.
Тем не менее, int(n)
намного более явственно, и если вы не делаете этого в критическом по времени цикле, int(n)
выигрывает его в читаемости над n // 1
. Разница во времени слишком мала, чтобы когнитивные затраты на то, чтобы определить, что // 1
здесь стоит.
Хотя Martijn Pieters ответил на ваш вопрос о том, что быстрее, и как его проверить, я чувствую, что скорость не так важна для такой небольшой операции. Я бы использовал int() для удобства чтения, как сказал Инбар Роуз. Обычно, когда речь идет о чем-то, эта небольшая читаемость гораздо важнее; хотя, общее уравнение может быть исключением из этого.
На самом деле 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
Обратите внимание, что вы не конвертируете с 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 в integer, я не думаю, что есть способ избить производительность int (n).
Просто статистический тест, чтобы немного повеселиться - измените тесты timeit на все, что вы предпочитаете:
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.'