Чому []
швидше, ніж 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
з міркувань продуктивності. Так ваша інтуїція про[]
і{}
передаючи назад фондову Літерал був неправий, але це не поширюється на()
і''
.