Время назначить переменную в Python

Предположим, у меня в очень плотном цикле есть следующее:

a = func(x)
b = func2(a)

Переменная a больше нигде не используется.

Компилирует ли Python присвоение a автоматически, или требуется время, чтобы каждый раз выполнять присвоение переменной? Другими словами, идентичен ли этот код или он немного быстрее из-за отсутствия присвоения a?

b = func2(func(x))

Такое же поведение для Python2.7 и Python3?

не уверен, но без присваивания можно было бы немного быстрее

Netwave 26.06.2018 17:06

Это похоже на вопрос, на который вы могли бы ответить, просто попробовав его самостоятельно (например, используя модуль время).

larsks 26.06.2018 17:11

@larsks Я читал, что timeit несколько ненадежен для таких небольших временных различий. Все ответы ниже, в которых он используется, показывают различия порядка наносекунд, что неубедительно и может быть просто шумом. Ответы на байт-код - это ответ, который я искал (и раньше я не знал о dis)

dkv 26.06.2018 17:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
688
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Такие запросы можно легко проверить с помощью timeit. Вот результаты с Python2.7.

root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.29 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.284 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.285 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.283 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.294 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.286 usec per loop

И это показывает согласованные результаты с другими ответами, в которых описывается использование замечательного модуля dis.

Вероятно, было бы разумнее поместить создание f1 и f2 в блок настройки, а не внутри цикла синхронизации.

user2357112 supports Monica 26.06.2018 17:24

Согласовано. Нет необходимости инициализировать для каждой итерации.

Riken Shah 26.06.2018 17:28
Ответ принят как подходящий

Итак, используя очень забавный модуль dis, мы можем посмотреть на фактический байт-код, который генерируется из предоставленного вами кода Python. Для простоты я заменил func и func2 встроенными функциями (int и float).

Итак, наш исходник выглядит так:

def assign():
    a = int()
    b = float(a)

По сравнению с упрощенной версией:

def simple():
    b = float(int())

А затем, начиная с интерпретатора cpython 2.7, мы можем увидеть байт-коды, сгенерированные функцией assign:

dis.dis(assign)
  2           0 LOAD_GLOBAL              0 (int)
              3 CALL_FUNCTION            0
              6 STORE_FAST               0 (a)

  3           9 LOAD_GLOBAL              1 (float)
             12 LOAD_FAST                0 (a)
             15 CALL_FUNCTION            1
             18 STORE_FAST               1 (b)
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE

Как вы можете видеть, для удаления ненужной промежуточной переменной нет никакой оптимизации, что приводит к дополнительным 2 инструкциям (STORE_FAST a, LOAD_FAST a) по сравнению с байт-кодами для упрощенного `простого метода:

dis.dis(simple)
  2           0 LOAD_GLOBAL              0 (float)
              3 LOAD_GLOBAL              1 (int)
              6 CALL_FUNCTION            0
              9 CALL_FUNCTION            1
             12 STORE_FAST               0 (b)
             15 LOAD_CONST               0 (None)
             18 RETURN_VALUE

То же самое для интерпретатора CPython для Python 3.5 и для интерпретатора pypy для Python 2.7.

Используйте модуль dis для сравнения байт-кода: похоже, что второй метод производит меньше операции

import dis

print(dis.dis('a=f(2);b=g(a)'))
print(dis.dis('b=g(f(2))'))


>>>   
  1           0 LOAD_NAME                0 (f)
              2 LOAD_CONST               0 (2)
              4 CALL_FUNCTION            1
              6 STORE_NAME               1 (a)
              8 LOAD_NAME                2 (g)
             10 LOAD_NAME                1 (a)
             12 CALL_FUNCTION            1
             14 STORE_NAME               3 (b)
             16 LOAD_CONST               1 (None)
             18 RETURN_VALUE
None
  1           0 LOAD_NAME                0 (g)
              2 LOAD_NAME                1 (f)
              4 LOAD_CONST               0 (2)
              6 CALL_FUNCTION            1
              8 CALL_FUNCTION            1
             10 STORE_NAME               2 (b)
             12 LOAD_CONST               1 (None)
             14 RETURN_VALUE
None

В основном то же самое, что и ответ @MatthewStory, но показывает другой способ использования dis.dis - я оставлю его, если не получу отрицательных ответов.

wwii 26.06.2018 17:25

Фактическое время будет зависеть от функций func() и func2(). Не лучший пример, но ниже приведен быстрый (и грязный) тестовый код:

import time

def func(x):
    return 5

def func2(a):
    return 10

t0 = time.time()
x = 10
for i in range(1,10000):
    a = func(x)
    b = func2(a)
t1 = time.time()

print("Time 1: ", t1-t0)

t2 = time.time()
x = 10
for i in range(1,10000):
    b = func2(func(x))
t3 = time.time()

print("Time 2: ", t3-t2)

Вывод приведенного выше кода:

Time 1:  0.0029211044311523438
Time 2:  0.002785921096801758

Так что да, реализация, в которой мы избегаем назначения a, немного быстрее в Pyhton 3.

Почему это должно зависеть от конкретных функций?

dkv 26.06.2018 17:58

Извините, я имел в виду, что фактическое время будет зависеть от содержимого функций. Отредактировал ответ.

manuignatius 27.06.2018 01:58

Другие вопросы по теме