Do czego służy funkcja id ()?

99

Przeczytałem dokumentację Python 2 i zauważyłem id()funkcję:

Zwróć „tożsamość” obiektu. Jest to liczba całkowita (lub długa liczba całkowita), która jest unikalna i stała dla tego obiektu przez cały okres jego istnienia. Dwa obiekty z nienakładającymi się okresami istnienia mogą mieć tę samą wartość id ().

Szczegóły implementacji CPythona: jest to adres obiektu w pamięci.

Tak więc eksperymentowałem, używając id()listy:

>>> list = [1,2,3]
>>> id(list[0])
31186196
>>> id(list[1])
31907092 // increased by 896
>>> id(list[2])
31907080 // decreased by 12

Jaka jest liczba całkowita zwrócona przez funkcję? Czy jest to synonimem adresów pamięci w C? Jeśli tak, dlaczego liczba całkowita nie odpowiada rozmiarowi typu danych?

Kiedy jest id()używany w praktyce?

Thanakron Tandavas
źródło
4
Tylko dlatego, że przechowujesz (powiedzmy) 32-bitową liczbę int w strukturze danych w języku skryptowym, nie oznacza to, że będziesz zużywać do 32 bitów więcej pamięci. ZAWSZE są dołączone metadane do WSZYSTKICH przechowywanych danych. rodzaj, rozmiar, długość, bla bla bla.
Marc B
cpython przydziela ze stosu, który zostaje zaszyfrowany, gdy obiekty są malloc'owane i uwolnione.
tdelaney
Liczby w Pythonie nie są prostymi danymi. Są to obiekty, które używają wewnętrznie longów na początku, a następnie automatycznie promują się do reprezentacji w stylu BigNumber, jeśli wartość stanie się zbyt duża.
Gareth Latty
1
Prawdopodobnie przydatne: Drastycznie

Odpowiedzi:

156

Twój post zawiera kilka pytań:

Jaka jest liczba zwrócona przez funkcję?

Jest to „ liczba całkowita (lub długa liczba całkowita), która na pewno będzie unikalna i stała dla tego obiektu przez cały okres jego życia. (Biblioteka standardowa języka Python - funkcje wbudowane) Unikalna liczba. Nic dodać nic ująć. Potraktuj to jako numer ubezpieczenia społecznego lub numer identyfikacyjny pracownika dla obiektów Pythona.

Czy to samo dotyczy adresów pamięci w C?

Koncepcyjnie tak, ponieważ obaj mają gwarancję, że będą wyjątkowi w swoim wszechświecie podczas swojego życia. W jednej konkretnej implementacji Pythona jest to adres pamięci odpowiedniego obiektu C.

Jeśli tak, dlaczego liczba nie wzrasta natychmiast o rozmiar typu danych (zakładam, że będzie to int)?

Ponieważ lista nie jest tablicą, a element listy to odniesienie, a nie obiekt.

Kiedy naprawdę używamy id( )funkcji?

Prawie nigdy. id()(lub jego odpowiednik) jest używany w isoperatorze.

Robᵩ
źródło
Używamy id () zwykle w celach demonstracyjnych, jak tutaj stackoverflow.com/questions/17246693/…
Nabin
49

To jest tożsamość lokalizacji obiektu w pamięci ...

Ten przykład może pomóc ci nieco lepiej zrozumieć koncepcję.

foo = 1
bar = foo
baz = bar
fii = 1

print id(foo)
print id(bar)
print id(baz)
print id(fii)

> 1532352
> 1532352
> 1532352
> 1532352

Wszystkie wskazują na to samo miejsce w pamięci, dlatego ich wartości są takie same. W tym przykładzie 1jest przechowywany tylko raz, a cokolwiek innego wskazuje, 1będzie odnosić się do tej lokalizacji pamięci.

brbcoding
źródło
9
Ale jeśli użyjesz liczb spoza zakresu od -5 do 256, nie otrzymasz tego samego identyfikatora dla zmiennej fii.
saurav
to jest niezwykle interesujące. Czy możesz udostępnić więcej?
jouell
3
Myślę, że ta odpowiedź jest myląca, ponieważ nie dotyczy to większości liczb; zobacz operator „is” zachowuje się nieoczekiwanie w przypadku liczb całkowitych .
kevinji,
8

id()zwraca adres obiektu, do którego się odwołujemy (w CPythonie), ale Twoje zamieszanie wynika z faktu, że listy w Pythonie bardzo różnią się od tablic w C. Na liście w Pythonie każdy element jest odniesieniem . Więc to, co robisz, jest znacznie bardziej podobne do tego kodu w C:

int *arr[3];
arr[0] = malloc(sizeof(int));
*arr[0] = 1;
arr[1] = malloc(sizeof(int));
*arr[1] = 2;
arr[2] = malloc(sizeof(int));
*arr[2] = 3;
printf("%p %p %p", arr[0], arr[1], arr[2]);

Innymi słowy, drukujesz adres z referencji, a nie adres odnoszący się do miejsca, w którym przechowywana jest Twoja lista.

W moim przypadku znalazłem id()funkcję przydatną do tworzenia nieprzezroczystych uchwytów, aby powrócić do kodu C podczas wywoływania pythonz C. Robiąc to, możesz łatwo użyć słownika do wyszukania obiektu z jego uchwytu i gwarantuje, że jest on unikalny.

Błąd krytyczny
źródło
7

Odpowiedź Roba (większość głosowała powyżej) jest poprawna. Chciałbym dodać, że w niektórych sytuacjach użycie identyfikatorów jest przydatne, ponieważ pozwala na porównanie obiektów i znalezienie, które obiekty odnoszą się do twoich obiektów.

Ta ostatnia zwykle pomaga na przykład w debugowaniu dziwnych błędów, w których zmienne obiekty są przekazywane jako parametr do wypowiadania klas i są przypisywane do lokalnych zmiennych w klasie. Mutowanie tych obiektów spowoduje mutację vars w klasie. Przejawia się to dziwnym zachowaniem, w którym wiele rzeczy zmienia się w tym samym czasie.

Niedawno miałem ten problem z aplikacją Python / Tkinter, w której edycja tekstu w jednym polu wprowadzania tekstu zmieniała tekst w innym podczas pisania :)

Oto przykład, jak możesz użyć funkcji id () do śledzenia, gdzie znajdują się te odwołania. Z całą pewnością nie jest to rozwiązanie obejmujące wszystkie możliwe przypadki, ale masz pomysł. Ponownie identyfikatory są używane w tle i użytkownik ich nie widzi:

class democlass:
    classvar = 24

    def __init__(self, var):
        self.instancevar1 = var
        self.instancevar2 = 42

    def whoreferencesmylocalvars(self, fromwhere):
        return {__l__: {__g__
                    for __g__ in fromwhere
                        if not callable(__g__) and id(eval(__g__)) == id(getattr(self,__l__))
                    }
                for __l__ in dir(self)
                    if not callable(getattr(self, __l__)) and __l__[-1] != '_'
                }

    def whoreferencesthisclassinstance(self, fromwhere):
        return {__g__
                    for __g__ in fromwhere
                        if not callable(__g__) and id(eval(__g__)) == id(self)
                }

a = [1,2,3,4]
b = a
c = b
democlassinstance = democlass(a)
d = democlassinstance
e = d
f = democlassinstance.classvar
g = democlassinstance.instancevar2

print( 'My class instance is of', type(democlassinstance), 'type.')
print( 'My instance vars are referenced by:', democlassinstance.whoreferencesmylocalvars(globals()) )
print( 'My class instance is referenced by:', democlassinstance.whoreferencesthisclassinstance(globals()) )

WYNIK:

My class instance is of <class '__main__.democlass'> type.
My instance vars are referenced by: {'instancevar2': {'g'}, 'classvar': {'f'}, 'instancevar1': {'a', 'c', 'b'}}
My class instance is referenced by: {'e', 'd', 'democlassinstance'}

Podkreślenia w nazwach zmiennych służą do zapobiegania kolizji nazw. Funkcje używają argumentu „skąd”, aby umożliwić im rozpoczęcie wyszukiwania odniesień. Ten argument jest wypełniany przez funkcję, która wyświetla wszystkie nazwy w danej przestrzeni nazw. Globals () jest jedną z takich funkcji.

Arijan
źródło
Świetne wyjaśnienie!
Neeraj Verma
5

Zaczynam od języka Python i używam id, kiedy używam powłoki interaktywnej, aby sprawdzić, czy moje zmienne są przypisane do tego samego elementu, czy po prostu wyglądają tak samo.

Każda wartość to identyfikator, czyli niepowtarzalna liczba związana z miejscem, w którym jest przechowywana w pamięci komputera.

lontgomjr
źródło
5

Jeśli używasz Pythona 3.4.1, otrzymasz inną odpowiedź na swoje pytanie.

list = [1,2,3]
id(list[0])
id(list[1])
id(list[2])

zwroty:

1705950792   
1705950808  # increased by 16   
1705950824  # increased by 16

Liczby całkowite, -5które 256mają stały id, a po znalezieniu go wiele razy jego identyfikator nie zmienia się, w przeciwieństwie do wszystkich innych liczb przed lub po nim, które mają inny identyfikator za każdym razem, gdy go znajdziesz. Liczby od -5do 256mają id w kolejności rosnącej i różnią się o 16.

Liczba zwracana przez id()funkcję jest unikalnym identyfikatorem nadawanym każdemu elementowi przechowywanemu w pamięci i jest analogicznie taka sama jak lokalizacja pamięci w C.

Dhvanan Shah
źródło
2

Odpowiedź brzmi: prawie nigdy. Identyfikatory są używane głównie wewnętrznie w Pythonie.

Przeciętny programista Pythona prawdopodobnie nigdy nie będzie musiał używać id()w swoim kodzie.

Gareth Latty
źródło
3
Może nie jestem przeciętny, ale używam id()całkiem sporo. Dwa przypadki użycia z góry mojej głowy: dyktando tożsamości (ręcznie rozwijane, ad-hoc) i zwyczaj repr()dla obiektu, w którym tożsamość ma znaczenie, ale wartość domyślna reprnie jest odpowiednia.
5
@delnan Nie twierdzę, że to częste przypadki.
Gareth Latty
2

isOperator wykorzystuje się do sprawdzenia, czy dwa identyczne przedmioty (w przeciwieństwie do równe). Rzeczywista wartość, która jest zwracana, id()prawie nigdy nie jest używana do niczego, ponieważ tak naprawdę nie ma znaczenia i jest zależna od platformy.

omz
źródło
2

Jest to adres obiektu w pamięci, dokładnie tak, jak mówi doktor. Ma jednak dołączone metadane, właściwości obiektu i lokalizacja w pamięci są potrzebne do przechowywania metadanych. Tak więc, kiedy tworzysz zmienną o nazwie lista, tworzysz także metadane dla listy i jej elementów.

Tak więc, jeśli nie jesteś absolutnym guru w języku, nie możesz określić id następnego elementu listy na podstawie poprzedniego elementu, ponieważ nie wiesz, co język przydziela wraz z elementami.

Lajos Arpad
źródło
1
W rzeczywistości bycie guru Pythona nie będzie zbyt przydatne w przewidywaniu obiektów id(). Musiałbyś dokładnie znać odpowiednie menedżery pamięci (liczba mnoga!), Znać ich dokładny stan w pewnym momencie i znać dokładną kolejność, w jakiej obiekty zostały przydzielone. Innymi słowy: nie będzie.
Jesteś absolutnym guru Pythona, jeśli znasz wszystkie jego niuanse, w tym możliwe menedżery pamięci.
Lajos Arpad
1
Znajomość Pythona i znajomość konkretnej implementacji (np. CPython) to dwie zupełnie różne rzeczy. I nawet znajomość CPythona od podszewki nie pomoże, ponieważ istnieje kilka wywołań CPythona zarządzających pamięcią, które nie są częścią CPythona, ale częścią odpowiedniego systemu operacyjnego. I jak powiedziałem wcześniej, nawet znajomość tych wszystkich rzeczy spoza Pythona nie pomoże, ponieważ rzeczywiste adresy zależą od stanu menedżerów pamięci w czasie wykonywania, a czasami są nawet losowe.
1
Nie wspominając o tym, że wiedza byłaby bezużyteczna, ponieważ poleganie na takiej rzeczy polegałoby na szczegółach implementacji, a zatem byłoby kruche i nieelastyczne.
Gareth Latty
2

Mam pomysł na wykorzystanie wartości id()w logowaniu.
Jest tani do zdobycia i jest dość krótki.
W moim przypadku używam tornado i id()chciałbym mieć kotwicę do grupowania wiadomości rozproszonych i mieszanych w plikach przez gniazdo sieciowe.

Daneel S. Yaitskov
źródło
0

Od w Pythonie 3 identyfikator jest przypisany do wartości, a nie do zmiennej. Oznacza to, że jeśli utworzysz dwie funkcje jak poniżej, wszystkie trzy id są takie same.

>>> def xyz():
...     q=123
...     print(id(q))
...
>>> def iop():
...     w=123
...     print(id(w))
>>> xyz()
1650376736
>>> iop()
1650376736
>>> id(123)
1650376736
Asish Kumar
źródło
Chociaż jest to sprzeczne z wcześniejszymi wyjaśnieniami, wydaje się, że tak się dzieje i nie wiem dlaczego. Jakieś wyjaśnienie? Na przykład x [0] = "abc" x [1] = "def" q = ["abc", "def"]. Id (x) == Id (q) jest True. Zbieg okoliczności?
VMMF
0

Trochę się spóźniłem i opowiem o Pythonie3. Aby zrozumieć, czym jest id () i jak działa (i Python), rozważ następny przykład:

>>> x=1000
>>> y=1000
>>> id(x)==id(y)
False
>>> id(x)
4312240944
>>> id(y)
4312240912
>>> id(1000)
4312241104
>>> x=1000
>>> id(x)
4312241104
>>> y=1000
>>> id(y)
4312241200

Musisz myśleć o wszystkim po prawej stronie jak o obiektach. Za każdym razem, gdy dokonujesz przypisania - tworzysz nowy obiekt, a to oznacza nowy identyfikator. W środku widać „dziki” obiekt, który jest tworzony tylko dla funkcji - id (1000). Tak więc jego żywotność dotyczy tylko tej linii kodu. Jeśli zaznaczysz następną linię - zobaczysz, że kiedy tworzymy nową zmienną x, ma ona taki sam identyfikator jak ten dziki obiekt. Prawie działa jak adres pamięci.

Tomas
źródło
0

Bądź ostrożny (jeśli chodzi o odpowiedź poniżej) ... To prawda tylko dlatego, że 123 jest między -5 a 256 ...

In [111]: q = 257                                                         

In [112]: id(q)                                                            
Out[112]: 140020248465168

In [113]: w = 257                                                         

In [114]: id(w)                                                           
Out[114]: 140020274622544

In [115]: id(257)                                                         
Out[115]: 140020274622768
Ojej
źródło