2013-03-03 19 views
10

Natknąłem się na dziwne zachowanie, które dzieje się tylko w interaktywnej sesji Pythona, ale nie wtedy, gdy piszę skrypt i go wykonuję.Python: zachowanie id() w Interpreter

String jest niezmienna typ danych w Pythonie, stąd:

>>> s2='string' 
>>> s1='string' 
>>> s1 is s2 
True 

Teraz dziwne część:

>>> s1='a string' 
>>> s2='a string' 
>>> s1 is s2 
False 

Widziałem, że ma spacji w ciągu powoduje to zachowanie. Jeśli umieściłem to w skrypcie i uruchomiłem, wynik jest prawdziwy w obu przypadkach.

Czy ktoś ma o tym pojęcia? Dzięki.

EDIT:

Ok, powyższe pytanie dać kilka pomysłów. Teraz tutaj jest inny eksperyment:

>>> s2='astringbstring' 
>>> s1='astringbstring' 
>>> s1 is s2 
True 

W tym przypadku ciągi są zdecydowanie dłużej niż 'a string', ale nadal występują te same identyfikatory.

+1

Zobacz ten post http://stackoverflow.com/questions/2123925/when-does-python-allocate-new-memory-for-identical-strings – isedev

+1

Należy pamiętać, że zasady interning mogą się różnić w poprzek implementacjach Python i wersjach. Oprócz idiomatycznego 'jest [nie] przypadku" use "jest niezwykle rzadkie w Pythonie; powinieneś go używać tylko wtedy, gdy naprawdę interesujesz się tożsamością obiektu, a nie równością wartości. –

Odpowiedz

6

Wielkie dzięki @eryksun za poprawki!

Wynika to z połączenia mechanizmu interning w Pythonie:

Wprowadź ciąg znaków w tabeli „internowanych” strun i powrót internowani String - który sam łańcuch lub kopii. Struny internowania są przydatne do uzyskania niewielkiej wydajności w wyszukiwaniu słownika - jeśli klucze są w internowaniu, a klucz wyszukiwania jest internowany, porównania kluczy (po mieszaniu) można wykonać za pomocą wskaźnika, porównując zamiast porównywanie ciągów. Zwykle nazwy używane w programach Python są automatycznie internowane, a słowniki używane do przechowywania atrybutów modułu, klasy lub instancji mają klucze internowane.

Zmieniono w wersji 2.3: Struny internowane nie są nieśmiertelne (podobnie jak były używane w Pythonie 2.2 i wcześniej); musisz zachować odniesienie do wartości zwracanej przez intern() wokół, aby z niej skorzystać.

CPython automatycznie intern krótkie pewne ciągi (ciągi 1 litera, słowo kluczowe, ciągi bez spacji, które zostały przypisane), aby zwiększyć prędkość odnośników i szybkość porównania:. Np 'dog' is 'dog' będzie porównanie wskaźnik zamiast pełnego porównania ciągów. Jednak automatyczne wstawianie dla wszystkich (dłuższych) łańcuchów wymaga znacznie więcej pamięci, co nie zawsze jest możliwe, a zatem mogą one nie mieć tej samej tożsamości, co powoduje, że wyniki różnią się od siebie, np.,:

# different id when not assigned 
In [146]: id('dog') 
Out[146]: 4380547672 

In [147]: id('dog') 
Out[147]: 4380547552 

# if assigned, the strings will be interned (though depends on implementation) 
In [148]: a = 'dog' 

In [149]: b = 'dog' 

In [150]: id(a) 
Out[150]: 4380547352 

In [151]: id(b) 
Out[151]: 4380547352 

In [152]: a is b 
Out[152]: True 

Dla liczb całkowitych, przynajmniej na moim komputerze, CPython automatycznie intern do 256 automatycznie:

In [18]: id(256) 
Out[18]: 140511109257408 

In [19]: id(256) 
Out[19]: 140511109257408 

In [20]: id(257) 
Out[20]: 140511112156576 

In [21]: id(257) 
Out[21]: 140511110188504 

UPDATE dzięki @eryksun: w tym przypadku napisu 'a string' nie jest internowany ponieważ CPython only interns strings without spaces, nie ze względu na długość, ponieważ od razu zakładałem: dla np. ASCII liter, cyfr i podkreślenia.

Aby uzyskać więcej informacji, możesz również zapoznać się z Alex Martelli's answer here.

+0

Dzięki za odpowiedź. Zobacz moją EDYCJA powyżej. –

+2

@Amit: To znak spacji w ciągu znaków. Obiekt kodu CPython będzie stażył tylko ciągi stałe, które są wszystkie "znaki nazw" (litery, cyfry i podkreślenia ASCII). Zobacz CPython 2.7.3 [codeobject.c] (http://hg.python.org/cpython/file/70274d53c1dd/Objects/codeobject.c#l71). – eryksun

+0

@eryksun dzięki za dodanie, zupełnie zapomniałem spacje nie są internowane. –

Powiązane problemy