Почему []
быстрее чем list()
?
Самая большая причина в том, что Python обрабатывает так list()
же, как пользовательскую функцию, что означает, что вы можете перехватить ее, добавив псевдоним в другое list
и сделать что-то другое (например, использовать свой собственный подклассовый список или, возможно, деку).
Он сразу создает новый экземпляр встроенного списка с помощью []
.
Мое объяснение стремится дать вам интуицию для этого.
объяснение
[]
широко известен как буквальный синтаксис.
В грамматике это называется «отображением списка». Из документов :
Отображение списка - это, возможно, пустая серия выражений, заключенная в квадратные скобки:
list_display ::= "[" [starred_list | comprehension] "]"
Отображение списка дает новый объект списка, содержимое которого задается либо списком выражений, либо пониманием. Когда указан список выражений через запятую, его элементы оцениваются слева направо и помещаются в объект списка в указанном порядке. Когда предоставляется понимание, список составляется из элементов, полученных в результате понимания.
Короче говоря, это означает, что создается встроенный объект типа list
.
Обойти это невозможно - это означает, что Python может сделать это так быстро, как может.
С другой стороны, list()
может быть перехвачен от создания встроенного list
с помощью встроенного конструктора списка.
Например, скажем, мы хотим, чтобы наши списки создавались с шумом:
class List(list):
def __init__(self, iterable=None):
if iterable is None:
super().__init__()
else:
super().__init__(iterable)
print('List initialized.')
Затем мы можем перехватить имя list
в глобальной области видимости на уровне модуля, а затем, когда мы создаем list
, мы фактически создаем наш подтипированный список:
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
Точно так же мы могли бы удалить его из глобального пространства имен
del list
и поместите его во встроенное пространство имен:
import builtins
builtins.list = List
И сейчас:
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
И обратите внимание, что отображение списка создает список безоговорочно:
>>> list_1 = []
>>> type(list_1)
<class 'list'>
Мы, вероятно, делаем это только временно, поэтому давайте отменим наши изменения - сначала удалим новый List
объект из встроенных:
>>> del builtins.list
>>> builtins.list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'builtins' has no attribute 'list'
>>> list()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'list' is not defined
О нет, мы потеряли след оригинала.
Не волнуйтесь, мы все еще можем получить list
- это тип литерала списка:
>>> builtins.list = type([])
>>> list()
[]
Так...
Почему []
быстрее чем list()
?
Как мы уже видели - мы можем перезаписать list
- но мы не можем перехватить создание литерального типа. Когда мы используем, list
мы должны сделать поиск, чтобы увидеть, есть ли что-нибудь.
Затем мы должны позвонить тому, что мы вызываем. Из грамматики:
Вызов вызывает вызываемый объект (например, функцию) с возможно пустой серией аргументов:
call ::= primary "(" [argument_list [","] | comprehension] ")"
Мы видим, что он делает то же самое для любого имени, а не только для списка:
>>> import dis
>>> dis.dis('list()')
1 0 LOAD_NAME 0 (list)
2 CALL_FUNCTION 0
4 RETURN_VALUE
>>> dis.dis('doesnotexist()')
1 0 LOAD_NAME 0 (doesnotexist)
2 CALL_FUNCTION 0
4 RETURN_VALUE
Поскольку []
на уровне байт-кода Python нет вызова функции:
>>> dis.dis('[]')
1 0 BUILD_LIST 0
2 RETURN_VALUE
Это просто идет прямо к построению списка без каких-либо поисков или вызовов на уровне байт-кода.
Вывод
Мы продемонстрировали, что list
его можно перехватить с помощью пользовательского кода, используя правила области видимости, который list()
ищет вызываемый объект и затем вызывает его.
Принимая во внимание, []
что это отображение списка или литерала, что позволяет избежать поиска имени и вызова функции.
()
и''
специальные, так как они не только пусты, они неизменны, и как таковой, это легко выиграть , чтобы сделать их одиночек; они даже не создают новые объекты, просто загружают синглтон для пустогоtuple
/str
. Технически детали реализации, но мне трудно представить, почему они не будут кешировать пустоеtuple
/str
по соображениям производительности. Так ваша интуиция о[]
и{}
передавая обратно фондовую Литерал был неправ, но это не распространяется на()
и''
.