Для чого використовується функція id ()?


99

Я прочитав документи Python 2 і помітив id()функцію:

Поверніть "ідентичність" об'єкта. Це ціле число (або довге ціле число), яке гарантовано буде унікальним і постійним для цього об'єкта протягом його життя. Два об'єкти із строками, що не перекриваються, можуть мати однакове значення id ().

Деталі реалізації CPython: Це адреса об'єкта в пам'яті.

Отже, я експериментував, використовуючи id()список:

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

Яке ціле число повертається з функції? Це синонім адресам пам'яті в С? Якщо так, то чому ціле число не відповідає розміру типу даних?

Коли id()використовується на практиці?


4
Тільки тому, що ви зберігаєте (скажімо) 32-бітовий int в структурі даних на мові сценаріїв, не означає, що ви будете використовувати на 32 біт більше пам'яті. Завжди метадані додані до БУДЬ-яких даних, які ви зберігаєте. тип, розмір, довжина, бла-бла-бла.
Марк Б

cpython виділяє з купи, яка стає зашифрованою, оскільки об'єкти є malloc'd та free'd.
tdelaney

Числа Python - це не прості фрагменти даних. Це об'єкти, які використовують для початку внутрішні терміни, а потім автоматично просувають до представлення у стилі BigNumber, якщо значення стає занадто великим.
Гарет Латті

Відповіді:


156

Ваше повідомлення задає кілька питань:

Яке число повертається з функції?

Це " ціле число (або довге ціле число), яке гарантовано буде унікальним і постійним для цього об'єкта протягом його життя ". (Стандартна бібліотека Python - Вбудовані функції) Унікальне число. Нічого більше, і нічого менше. Подумайте про це як номер соціального захисту або ідентифікаційний номер працівника для об’єктів Python.

Це те ж саме з адресами пам'яті в С?

Концептуально, так, тим, що їм обом гарантовано бути унікальними у їх Всесвіті протягом життя. І в одній конкретній реалізації Python це фактично адреса пам'яті відповідного об'єкта C.

Якщо так, то чому число не збільшується миттєво на розмір типу даних (я припускаю, що це було б int)?

Тому що список - це не масив, а елемент списку - це посилання, а не об’єкт.

Коли ми дійсно використовуємо id( )функцію?

Майже ніколи. id()(або його еквівалент) використовується в isоператорі.


Ми використовуємо id () зазвичай для демонстраційних цілей, наприклад, тут stackoverflow.com/questions/17246693/…
Nabin

49

Ось ідентичність місцезнаходження об'єкта в пам'яті ...

Цей приклад може допомогти вам зрозуміти концепцію трохи більше.

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

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

> 1532352
> 1532352
> 1532352
> 1532352

Усі вони вказують на одне і те ж місце в пам'яті, тому їх значення однакові. У прикладі 1зберігається лише один раз, і все, що вказує на 1це, посилається на це місце пам'яті.


9
Але якщо ви будете використовувати цифри за межами від -5 до 256, ви не отримаєте однакового ідентифікатора для змінної fii.
saurav

це надзвичайно цікаво. Чи можете ви поділитися більше?
jouell

3
Я думаю, що ця відповідь вводить в оману, оскільки це не відповідає дійсності більшості номерів; див. "є", оператор поводиться несподівано з цілими числами .
кевінджі

8

id()повертає адресу об'єкта, на який посилається (у CPython), але ваше плутанина виникає через те, що списки python сильно відрізняються від масивів C. У списку пітонів кожен елемент є посиланням . Тож те, що ви робите, набагато більше схоже на цей код 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]);

Іншими словами, ви друкуєте адресу з посилання, а не адресу щодо місця, де зберігається ваш список.

У моєму випадку я знайшов id()функцію, зручну для створення непрозорих ручок для повернення до коду С при виклику pythonз C. Зробивши це, ви можете легко використовувати словник для пошуку об’єкта з його ручки, і це гарантовано унікальність.


7

Відповідь Роб (більшість голосуючих вище) правильна. Я хотів би додати, що в деяких ситуаціях використання ідентифікаторів є корисним, оскільки дозволяє порівняти об'єкти та знайти, які об’єкти відносяться до ваших об'єктів.

Пізніше зазвичай допомагає вам, наприклад, налагоджувати дивні помилки, коли змінні об’єкти передаються як параметр для класів, що говорять, і призначаються місцевим парам в класі. Вимкнення цих об'єктів буде мутувати vars у класі. Це проявляється в дивній поведінці, коли одночасно змінюється багато речей.

Нещодавно у мене виникла ця проблема з додатком Python / Tkinter, коли редагування тексту в одному полі введення тексту змінило текст в інше, як я вводив :)

Ось приклад того, як ви можете використовувати функцію id (), щоб простежити, де знаходяться ці посилання. Це, безумовно, не є рішенням, яке охоплює всі можливі випадки, але ви отримуєте ідею. Знову ідентифікатори використовуються у фоновому режимі, і користувач їх не бачить:

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()) )

ВИХІД:

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'}

Підкреслення в назвах змінних використовується для запобігання зіткнення імен. Функції використовують аргумент "звідки", щоб ви могли повідомити їм, з чого почати пошук посилань. Цей аргумент заповнюється функцією, яка перераховує всі імена в заданому просторі імен. Глобали () - одна з таких функцій.


Чудове пояснення!
Neeraj Verma

5

Я починаю з python і використовую ідентифікатор, коли використовую інтерактивну оболонку, щоб побачити, чи мої змінні присвоєні одній і тій же речі, чи вони просто виглядають однаково.

Кожне значення - це ідентифікатор, який є унікальним номером, пов’язаним з тим, де воно зберігається в пам'яті комп’ютера.


5

Якщо ви використовуєте python 3.4.1, тоді ви отримаєте іншу відповідь на своє запитання.

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

повертає:

1705950792   
1705950808  # increased by 16   
1705950824  # increased by 16

Цілі , -5щоб 256мати постійний ідентифікатор, і на пошук його кілька разів його ідентифікатор не змінюється, в відміну від всіх інших чисел до або після того , що мають різні ідентифікатори кожен раз , коли ви знайдете його. Числа від, -5щоб 256мати ідентифікатори, зростає в порядку і відрізняється на16 .

Число, яке повертається id()функцією, є унікальним ідентифікатором, що надається кожному елементу, що зберігається в пам'яті, і аналогічно тому, як місце в пам'яті в C.


2

Відповідь майже ніколи. Ідентифікатори в основному використовуються внутрішньо для Python.

Пересічному програмісту Python, ймовірно, ніколи не потрібно використовувати id()у своєму коді.


3
Можливо, я не середній, але використовую id()зовсім небагато. Два випадки використання вгорі моєї голови: A (вручну прокручений, спеціальний) ідентифікаційний наказ і звичай repr()для об'єкта, де особистість має значення, але за замовчуванням reprне підходить.

5
@delnan Я б не заперечував, що це звичайні випадки.
Гарет Латті

2

isОператор використовує його , щоб перевірити , є чи два об'єкти ідентичні (на відміну від рівних). Фактичне значення, яке повертається id(), майже ніколи не використовується для нічого, оскільки воно насправді не має сенсу, і це залежить від платформи.


2

Це адреса об'єкта в пам'яті, точно так, як каже док. Однак до нього додаються метадані, властивості об'єкта та розташування в пам'яті необхідні для зберігання метаданих. Отже, коли ви створюєте змінну, що називається списком, ви також створюєте метадані для списку та його елементів.

Тож, якщо ви не абсолютний гуру в мові, ви не можете визначити ідентифікатор наступного елемента списку на основі попереднього елемента, оскільки ви не знаєте, що виділяє мова разом з елементами.


1
Насправді, бути гуру Python не буде корисно передбачити будь-який об'єкт id(). Вам потрібно буде добре ознайомитись з відповідними менеджерами пам'яті (множина!), Знати їх точний стан у певний момент та знати точний порядок розподілу об'єктів. Іншими словами: не відбудеться.

Ви абсолютний гуру Python, якщо абсолютно знаєте всі його нюанси, включаючи можливих менеджерів пам'яті.
Лайош Арпад

1
Знання Python та знання конкретної реалізації (наприклад, CPython) - це дві абсолютно різні речі. І навіть знання CPython зсередини не допоможе, оскільки є кілька менеджерів пам'яті, що викликають CPython, які не є частиною CPython, а є частиною відповідної операційної системи. І, як я вже говорив, навіть знаючи всі ці не-Python речі вам не допоможе, оскільки фактичні адреси залежать від стану роботи менеджерів пам'яті, а іноді навіть є рандомізованими.

1
Не кажучи вже про те, що було б марно знати, так як покладатися на таке, що спирається на деталі реалізації, а отже, є крихким і негнучким.
Гарет Латті

2

У мене є ідея використовувати значення для id()ведення журналів.
Її дістати дешево, і вона досить коротка.
У моєму випадку я використовую торнадо і id()хотів би прив’язати до групових повідомлень, розкиданих і змішаних по файлу веб-сокетом.


0

Станом на python 3 id присвоюється значенню, а не змінній. Це означає, що якщо ви створите дві функції, як показано нижче, всі три ідентичні елементи однакові.

>>> def xyz():
...     q=123
...     print(id(q))
...
>>> def iop():
...     w=123
...     print(id(w))
>>> xyz()
1650376736
>>> iop()
1650376736
>>> id(123)
1650376736

Незважаючи на те, що це суперечить попереднім поясненням, здається, це відбувається, і я не знаю чому. Будь-яке пояснення? Наприклад x [0] = "abc" x [1] = "def" q = ["abc", "def"]. Id (x) == Id (q) - Істинно. Збіг?
VMMF

0

Я трохи запізнююся, і я поговорю про Python3. Щоб зрозуміти, що таке id (), і як він (і Python) працює, розглянемо наступний приклад:

>>> 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

Вам потрібно подумати про все з правого боку як про предмети. Кожен раз, коли ви робите завдання - ви створюєте новий об'єкт, а це означає новий ідентифікатор. У середині ви можете побачити "дикий" об'єкт, створений лише для функції - id (1000). Отже, термін експлуатації призначений лише для цього рядка коду. Якщо ви перевірте наступний рядок - ви побачите, що коли ми створюємо нову змінну x, вона має той самий ідентифікатор, що і цей дикий об’єкт. В значній мірі це працює як адреса пам'яті.


0

Будьте обережні (щодо відповіді трохи нижче) ... Це справедливо лише тому, що 123 - від -5 до 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.