Здесь можно задать две разные вещи.
Вторая будет быстрее только из-за таких вещей, как:
- Вместо передачи одного аргументаиз 1000. (В последних версиях CPython это на самом деле не означает пропуск 1000 значений в стеке, но это означает, что вам нужно пройти через
CALL_FUNCTION_EX
вместо того, чтобы пройти через простой CALL_FUNCTION
, и на самом деле его оптимизироватьбыстрый путь.) - Цикл по элементам списка внутри оптимизированного
list.__repr__
, а не с помощью общего цикла итератора. - Материал, который
print
выполняет между __str__
каждого элементанемного сложнее, чем то, что list.__repr__
делает между __repr__
каждого элемента (условно печатая локальную переменную, вместо того, чтобы всегда привязывать постоянную строку).
Это должно бытьвопрос микросекунд - достаточно для измерения , но недостаточно для уведомления .
Но второе будет также быстрее bпотому что он делает меньше вызовов ввода / вывода.Это может затмить все эти небольшие различия.И если ваш терминал работает медленно, например, Windows cmd
или поддельный терминал IDLE, этого может быть достаточно легко заметить.
Сначала давайте попробуем вызвать функцию, которая абсолютно ничего не делает:
In [765]: def dummy(*args): pass
In [766]: %timeit dummy(*range(1, 1001))
19 µs ± 71.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [767]: %timeit dummy([*range(1, 1001)])
13 µs ± 609 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Итак, второй примерно на 50% быстрее только из-за передачи аргументов, но это всего лишь 6us.
Что, если он итерирует свои аргументы, фактически print
делает?
In [768]: def dummy(*args):
...: for _ in args: pass
In [769]: %timeit dummy(*range(1, 1001))
22.8 µs ± 1.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [770]: %timeit dummy([*range(1, 1001)])
13.1 µs ± 148 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Теперь она приближается к 2: 1, но разница все равно составляет всего 9 единиц.
Конечно, я немного обманываю, потому что print
- это Cфункция - но, в любом случае, *args
превращается в кортеж, который они должны каким-то образом циклически повторять.
Что, если он также вызывает __str__
каждого из своих аргументов, дляболее справедливое сравнение?
In [776]: def dummy(*args):
...: for arg in args: str(arg)
In [776]: %timeit dummy(*range(1, 1001))
185 µs ± 1.67 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [777]: %timeit dummy([*range(1, 1001)])
86.3 µs ± 826 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Даже пытаясь сделать вещи более справедливыми по отношению к первому, оно все равно примерно 2: 1.
Давайте на самом деле назовем print
, но напечатаемк объекту файла, который просто выбрасывает свои входные данные:
In [747]: class Nully:
...: def write(self, *args): pass
In [749]: null = Nully()
In [750]: %timeit print(*range(1, 1001), file=null)
390 µs ± 7.44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [751]: %timeit print([*range(1, 1001)], file=null)
88.4 µs ± 2.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Теперь второй примерно в 4 раза быстрее, но мы все еще говорим лишь доли миллисекунды.
Теперь давайте попробуем подключить фактический ввод-вывод, но к нулевому устройству:
In [745]: %timeit with open(os.devnull, 'w') as null: print(*range(1, 1001), file=null)
436 µs ± 13.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [746]: %timeit with open(os.devnull, 'w') as null: print([*range(1, 1001)], file=null)
140 µs ± 1.74 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Они оба замедлились примерно на одинаковую небольшую величину.
Теперь давайте попробуем записать в файл, который занимает много времени - скажем, 10 мс - для каждой записи:
In [767]: class Slowy:
...: def write(self, *args): time.sleep(0.01)
In [768]: null = Slowy()
In [770]: %timeit print(*range(1, 1001), file=null)
26.8 s ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [771]: %timeit print([*range(1, 1001)], file=null)
28.2 ms ± 39.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Первоначальная разница в 300us, вероятно, все еще существует, но кого это волнует?Здесь имеет значение разница в 3 порядка на 27 секунд, вызванная 1000 операций записи вместо 1.
Конечно, даже cmd.exe
и IDLE не , что медленно.Но они довольно медленные.
Итак, я думаю, что последняя часть - это то, о чем вы спрашиваете.
Фактически, из комментариев, добавленных позже:
Это заметная задержка человека.
timeit.timeit("print(*range(1,1001))",number=1) => 10 s
timeit.timeit("print([*range(1,1001)])",number=1) => 95 ms
Кстати, я использую Windows и запускаю этот код на Python IDLE.
Так что я ошибся: IDLE равно почти точно так медленно.(Ничего себе!)