Dlaczego jest []
szybszy niż list()
?
Największym powodem jest to, że Python traktuje list()
jak funkcję zdefiniowaną przez użytkownika, co oznacza, że możesz ją przechwycić, aliasingując coś innego list
i zrobić coś innego (np. Użyć własnej listy w sklasyfikowanej klasie lub być może deque).
Natychmiast tworzy nowe wystąpienie wbudowanej listy za pomocą []
.
Moje wyjaśnienie ma na celu dać ci do tego intuicję.
Wyjaśnienie
[]
jest powszechnie znany jako dosłowna składnia.
W gramatyce jest to określane jako „wyświetlanie listy”. Z dokumentów :
Wyświetlanie listy jest prawdopodobnie pustą serią wyrażeń zawartych w nawiasach kwadratowych:
list_display ::= "[" [starred_list | comprehension] "]"
Wyświetlanie listy daje nowy obiekt listy, a treść jest określana albo przez listę wyrażeń, albo przez zrozumienie. Gdy podana jest lista wyrażeń oddzielona przecinkami, jej elementy są oceniane od lewej do prawej i umieszczane w obiekcie listy w tej kolejności. Kiedy dostarczane jest zrozumienie, lista jest tworzona z elementów wynikających ze zrozumienia.
Krótko mówiąc, oznacza to, że list
tworzony jest wbudowany obiekt typu .
Nie można tego obejść - co oznacza, że Python może to zrobić tak szybko, jak to możliwe.
Z drugiej strony list()
można przechwycić tworzenie wbudowanego programu list
za pomocą wbudowanego konstruktora listy.
Załóżmy na przykład, że chcemy, aby nasze listy były tworzone głośno:
class List(list):
def __init__(self, iterable=None):
if iterable is None:
super().__init__()
else:
super().__init__(iterable)
print('List initialized.')
Możemy wtedy przechwycić nazwę list
w zasięgu globalnym na poziomie modułu, a następnie, gdy tworzymy list
, tworzymy naszą listę podtypów:
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
Podobnie możemy usunąć go z globalnej przestrzeni nazw
del list
i umieść go we wbudowanej przestrzeni nazw:
import builtins
builtins.list = List
I teraz:
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
I zauważ, że wyświetlenie listy tworzy listę bezwarunkowo:
>>> list_1 = []
>>> type(list_1)
<class 'list'>
Prawdopodobnie robimy to tylko tymczasowo, więc List
cofnijmy nasze zmiany - najpierw usuń nowy obiekt z wbudowanych:
>>> 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
Och, nie, zgubiliśmy oryginał.
Nie martw się, wciąż możemy uzyskać list
- to rodzaj literału listy:
>>> builtins.list = type([])
>>> list()
[]
Więc...
Dlaczego jest []
szybszy niż list()
?
Jak widzieliśmy - możemy nadpisać list
- ale nie możemy przechwycić stworzenia literalnego typu. Kiedy korzystamy, list
musimy wyszukiwać, aby sprawdzić, czy coś tam jest.
Następnie musimy zadzwonić do każdej wywoływanej przez nas funkcji. Z gramatyki:
Wywołanie wywołuje wywoływalny obiekt (np. Funkcję) z możliwie pustą serią argumentów:
call ::= primary "(" [argument_list [","] | comprehension] ")"
Widzimy, że robi to samo dla dowolnej nazwy, nie tylko listy:
>>> 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
Ponieważ []
na poziomie kodu bajtowego języka Python nie ma wywołania funkcji:
>>> dis.dis('[]')
1 0 BUILD_LIST 0
2 RETURN_VALUE
Po prostu idzie prosto do budowania listy bez wyszukiwania lub wywołań na poziomie kodu bajtowego.
Wniosek
Wykazaliśmy, że list
można przechwycić kod użytkownika za pomocą reguł określania zakresu, który list()
wyszukuje wywołanie, a następnie je wywołuje.
Natomiast []
wyświetla listę lub dosłownie, dzięki czemu unika się wyszukiwania nazwy i wywoływania funkcji.
()
i''
są wyjątkowe, ponieważ są nie tylko puste, są niezmienne i dlatego łatwo jest je wygrać; nawet nie budują nowych obiektów, po prostu ładują singleton dla pustychtuple
/str
. Technicznie szczegół implementacji, ale trudno mi sobie wyobrazić, dlaczego nie buforują pustychtuple
/str
ze względu na wydajność. Więc twoja intuicja[]
i{}
przekazywanie literału podstawowego było błędne, ale dotyczy to()
i''
.