Подтвердить что ты не робот

Каков эффективный способ преобразования float в int в python?

Я использовал n = int(n) для преобразования a float в int.

Недавно я встретил другой способ сделать то же самое:

n = n // 1

Каков наиболее эффективный способ и почему?

4b9b3361

Ответ 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 здесь стоит.

Ответ 2

Хотя Martijn Pieters ответил на ваш вопрос о том, что быстрее, и как его проверить, я чувствую, что скорость не так важна для такой небольшой операции. Я бы использовал int() для удобства чтения, как сказал Инбар Роуз. Обычно, когда речь идет о чем-то, эта небольшая читаемость гораздо важнее; хотя, общее уравнение может быть исключением из этого.

Ответ 3

На самом деле 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

Ответ 4

Обратите внимание, что вы не конвертируете с 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).

Ответ 5

Просто статистический тест, чтобы немного повеселиться - измените тесты 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.'