logo

Покажчик у Python | Чому Python не підтримує покажчик

У цьому підручнику ми дізнаємося про вказівник у Python і з’ясуємо, чому Python не підтримує концепції вказівника.

Ми також зрозуміємо, як ми можемо симулювати вказівник на Python. Нижче представлено вказівник для тих, хто нічого про нього не знає.

Ми також зрозуміємо, як ми можемо симулювати вказівник на Python. Нижче представлено вказівник для тих, хто нічого про нього не знає.

Що таке покажчик?

Покажчик є дуже популярним і корисним інструментом для зберігання адреси змінної. Якщо хтось коли-небудь працював з мовою низького рівня, наприклад C . C++ , він/вона, ймовірно, були б знайомі з покажчиками. Він дуже ефективно керує кодом. Це може бути трохи складно для початківців, але це одна з важливих концепцій програми. Однак це може призвести до різних помилок керування пам’яттю. Таким чином, визначення покажчиків -

'Покажчики - це змінні, які містять адресу пам'яті іншої змінної. Змінні покажчика позначаються зірочкою (*).'

Розглянемо наступний приклад покажчика на мові програмування C.

Приклад. Як використовувати покажчик у C

 #include int main() { int* po, o; 0 = 10; printf('Address of c: %p
', &c); printf('Value of c: %d

', c); o = &0; printf('Address of pointer pc: %p
', o); printf('Content of pointer pc: %d

', *o); 0 = 11; printf('Address of pointer pc: %p
', p0); printf('Content of pointer pc: %d

', *p0); *po = 2; printf('Address of c: %p
', &o); printf('Value of c: %d

', o); return 0; } 

Вихід:

Address of o: 2686784 Value of o: 22 Address of pointer po: 2686784 Content of pointer po: 22 Address of pointer po: 2686784 Content of pointer po: 11 Address of o: 2686784 Value of o: 2 

Окрім того, що покажчики корисні, вони не використовуються в Python . У цій темі ми обговоримо об’єктну модель Python і дізнаємось, чому покажчики в Python не існують. Ми також дізнаємось про різні способи імітації вказівників у Python. Спочатку обговоримо, чому Python не підтримує покажчики.

Чому Python не підтримує покажчики

Точна причина непідтримки покажчика не зрозуміла. Чи може вказівник у Python існувати нативно? Основною концепцією Python є його простота, але покажчик порушує Дзен Python. Вказівники переважно заохочуються до неявних змін, а не до явних. Вони теж складні, особливо для новачків.

Покажчики, як правило, створюють складність у коді, де Python головним чином зосереджується на зручності використання, а не на швидкості. Як результат, Python не підтримує покажчик. Однак Python дає деякі переваги використання покажчика.

Перш ніж зрозуміти покажчик у Python, нам потрібно мати основне уявлення про наступні моменти.

  • Незмінні проти змінних об’єктів
  • Змінні/імена Python

Об'єкти в Python

У Python все є об’єктом, навіть клас, функції, змінні тощо. Кожен об’єкт містить щонайменше три частини даних.

Команда cp в Linux
  • Кількість посилань
  • Тип
  • Значення

Давайте обговоримо по черзі.

Кількість посилань - Використовується для управління пам'яттю. Щоб отримати більше інформації про керування пам’яттю Python, прочитайте Керування пам’яттю в Python.

Тип - The CPython шар використовується як тип для забезпечення безпеки типу під час виконання. Нарешті, є значення, яке є фактичним значенням, пов’язаним з об’єктом.

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

Незмінні та змінні об’єкти

Незмінні об’єкти не можна змінювати, а змінні об’єкти можна змінювати. Давайте подивимось наведену нижче таблицю поширених типів і те, чи є вони змінними чи ні.

Об'єкти Тип
Міжн Незмінний
Поплавок Незмінний
Bool Незмінний
Список Змінний
встановити Змінний
Комплекс Змінний
Кортеж Незмінний
Frozenset Незмінний
Dict Змінний

Ми можемо перевірити тип вищезазначених об’єктів за допомогою id() метод. Цей метод повертає адресу пам'яті об'єкта.

Ми вводимо наведені нижче рядки в середовищі REPL.

 x = 5 id(x) 

Вихід:

140720979625920 

У наведеному вище коді ми присвоїли x значення 10. якби ми змінили це значення за допомогою підстановки, ми б отримали нові об’єкти.

 x-=1 id(x) 

Вихід:

140720979625888 

Як ми бачимо, ми змінюємо наведений вище код і отримуємо нові об’єкти як відповідь. Візьмемо інший приклад вул .

мій фліксер
 s = 'java' print(id(s)) s += 'Tpoint' print(s) id(s) 

Вихід:

2315970974512 JavaTpoint 1977728175088 

Знову ми змінюємо значення x, додаючи новий рядок, і отримуємо нову адресу пам’яті. Давайте спробуємо додати рядок безпосередньо в s.

 s = 'java' s[0] = T print(id(s)) 

Вихід:

Traceback (most recent call last): File 'C:/Users/DEVANSH SHARMA/PycharmProjects/MyPythonProject/python1.py', line 34, in s[0] = T NameError: name 'T' is not defined 

Наведений вище код повертає помилку, це означає, що рядок не підтримує мутацію. Так вул є незмінними об'єктами.

Тепер ми побачимо змінний об’єкт, наприклад список.

 my_list = [3, 4, 8] print(id(my_list)) my_list.append(4) print(my_list) print(id(my_list)) 

Вихід:

2571132658944 [3, 4, 8, 4] 2571132658944 

Як ми бачимо в коді вище, мій_список має початковий ідентифікатор, і ми додали 5 до списку; мій_список має той самий ідентифікатор, оскільки список підтримує мінливість.

Розуміння змінних Python

Спосіб визначення змінних у Python значно відрізняється від C або C++. Змінна Python не визначає тип даних. Насправді Python має імена, а не змінні.

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

Давайте розберемося, як змінна працює в C і як ім'я працює в Python.

Змінні в C

У мові C змінна означає, що вона зберігає значення або зберігає значення. Він визначається типом даних. Давайте розглянемо наступний код, який визначає змінну.

 int x = 286; 
  • Виділіть достатньо пам'яті для цілого числа.
  • Ми присвоюємо цій ділянці пам’яті значення 286.
  • X представляє це значення.

Якщо ми представляємо погляд пам'яті -

Покажчик на Python

Як ми бачимо, x має місце в пам’яті для значення 286. Тепер ми призначимо нове значення x.

х = 250

Це нове значення перезаписує попереднє значення. Це означає, що змінна x є змінною.

Розташування значення x те саме, але значення змінилося. Це важливий момент, який вказує на те, що x є місцем пам'яті, а не просто його назвою.

період ключ

Тепер ми вводимо нову змінну, яка приймає x, а потім y створює новий блок пам’яті.

 int y = x; 

Змінна y створює новий блок під назвою y, копіює значення з x у поле.

Покажчик на Python

Імена в Python

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

 x = 289 

Наведений вище код зламано під час виконання.

  1. Створіть PyObject
  2. Встановіть для PyObject ціле число для типу typecode
  3. Встановіть значення 289 для PyObject
  4. Створіть назву x
  5. Наведіть x на новий PyObject
  6. Збільште refcount для PyObject на 1

Це буде виглядати так, як показано нижче.

Покажчик на Python

Ми можемо зрозуміти внутрішню роботу змінної в Python. Змінна x вказує на посилання на об’єкт, і вона не має місця в пам’яті, як раніше. Це також показує, що x = 289 прив’язує ім’я x до посилання.

Тепер ми вводимо нову змінну та призначаємо їй x.

 y = x 

У Python змінна y не створить новий об’єкт; це просто нова назва, що вказує на той самий об’єкт. Об'єкт refcount також збільшився на одиницю. Ми можемо це підтвердити наступним чином.

 y is x 

Вихід:

True 

Якщо ми збільшимо значення y на одиницю, воно більше не посилатиметься на той самий об’єкт.

 y + =1 y is x 

Це означає, що в Python ми не призначаємо змінні. Натомість ми прив’язуємо імена до посилань.

Симуляція вказівників у Python

Як ми вже обговорювали, Python не підтримує покажчик, але ми можемо отримати переваги використання покажчика. Python надає альтернативні способи використання покажчика в Python. Ці два способи наведено нижче.

  • Використання змінних типів як покажчиків
  • Використання власних об’єктів Python

Давайте розберемося в наведених моментах.

Використання мінливих типів як покажчика

У попередньому розділі ми визначили об’єкти змінного типу; ми можемо розглядати їх як покажчики, щоб імітувати поведінку покажчика. Давайте розберемося в наступному прикладі.

C

 void add_one(int *a) { *a += 1; } 

У наведеному вище коді ми визначили покажчик *a, а потім збільшили значення на одиницю. Тепер ми реалізуємо це за допомогою функції main().

операційна система linux
 #include int main(void) { int y = 233; printf('y = %d
', y); add_one(&y); printf('y = %d
', y); return 0; } 

Вихід:

y = 233 y = 234 

Ми можемо імітувати цей тип поведінки за допомогою змінного типу Python. Зрозумійте наступний приклад.

 def add_one(x): x[0] += 1 y = [2337] add_one(y) y[0] 

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

 z = (2337,) add_one(z) 

Вихід:

Traceback (most recent call last): File '', line 1, in File '', line 2, in add_one TypeError: 'tuple' object does not support item assignment 

Ми використали кортеж у коді вище, незмінний об’єкт, тому він повернув помилку. Ми також можемо використовувати словник для імітації покажчика в Python.

Давайте розберемо наступний приклад, де ми підраховуватимемо кожну операцію, яка відбувається в програмі. Ми можемо використовувати dict для досягнення цього.

приклад -

 count = {'funcCalls': 0} def car(): count['funcCalls'] += 1 def foo(): count['funCcalls'] += 1 car() foo() count['funcCalls'] 

Вихід:

2 

Пояснення -

У наведеному вище прикладі ми використали рахувати словник, який відстежував кількість викликів функцій. Коли foo() викликається функція, лічильник збільшується на 2, оскільки dict є змінним.

Використання об’єктів Python

У попередньому прикладі ми використовували dict для емуляції вказівника в Python, але іноді стає важко запам’ятати всі використані імена ключів. Ми можемо використовувати спеціальний клас Python замість словника. Давайте розберемося в наступному прикладі.

приклад -

 class Pointer(object): def __init__(self): self._metrics = { 'funCalls': 0, 'catPictures': 0, } 

У наведеному вище коді ми визначили клас Pointer. Цей клас використовував dict для зберігання фактичних даних у змінній-члені _metrics. Це забезпечить змінність нашої програми. Ми можемо зробити це наступним чином.

 class Pointer(object): # ... @property def funCalls(self): return self._metrics['func_calls'] @property def catPictures_served(self): return self._metrics['cat_pictures_served'] 

Ми використали @власність декоратор. Якщо ви не знайомі з декораторами, відвідайте наш посібник із декораторів Python. Декоратор @property отримає доступ до funCalls і catPicture_served. Тепер ми створимо об’єкт класу Pointer.

 pt = Pointer() pt.funCalls() pt.catPicture_served 

Тут нам потрібно збільшити ці значення.

 class Pointer(object): # ... def increament(self): self._metrices['funCalls'] += 1 def cat_pics(self): self._metrices['catPictures_served'] += 1 

Ми визначили два нові методи - increment() і cat_pics(). Ми змінили значення за допомогою цих функцій у матрицях dict. Тут ми можемо змінити клас так само, як змінюємо покажчик.

 pt = Pointer() pt.increment() pt.increment() pt.funCalls() 

Модуль Python ctypes

Модуль Python ctypes дозволяє нам створити покажчик C-типу в Python. Цей модуль корисний, якщо ми хочемо зробити виклик функції до бібліотеки C, для якої потрібен покажчик. Давайте розберемося в наступному прикладі.

Приклад - мова C

 void incr_one(int *x) { *x += 1; } 

У наведеній вище функції ми збільшили значення x на одиницю. Припустімо, що ми зберігаємо наведений вище файл під назвою incrPointer.c і вводимо наступну команду в терміналі.

 $ gcc -c -Wall -Werror -fpic incrPointer.c $ gcc -shared -o libinc.so incrPointer.o 

Перша команда компілюється incrPointer.c в об’єкт, який називається incrPointer.o. Друга команда приймає об’єктний файл і створює libinic.so для співпраці з ctypes.

матриці в програмуванні на C
 import ctypes ## libinc.so library should be same directory as this program lib = ctypes.CDLL('./libinc.so') lib.increment 

Вихід:

 

У наведеному вище коді ctypes.CDLL повертає спільний об'єкт, який називається libinic.so. Він містить incrPointer() функція. Якщо нам потрібно вказати вказівник на функції, які ми визначаємо в спільному об’єкті, ми повинні вказати його за допомогою ctypes. Давайте розглянемо наведений нижче приклад.

 inc = lib.increment ## defining the argtypes inc.argtypes = [ctypes.POINTER(ctypes.c_int)] 

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

 incrPointer(10) 

Вихід:

Traceback (most recent call last): File '', line 1, in ctypes.ArgumentError: argument 1: : expected LP_c_int instance instead of int 

Це тому, що для incrPointer потрібен вказівник, а ctypes — це спосіб передачі вказівника в Python.

 v = ctypes.c_int(10) 

v є змінною C. Ctypes забезпечує виклик методу byref() який раніше передавав посилання на змінну.

 inc(ctypes.byref(a)) a 

Вихід:

c_int(11) 

Ми збільшили значення за допомогою еталонної змінної.

Висновок

Ми обговорювали, що вказівник відсутній у Python, але ми можемо реалізувати ту саму поведінку за допомогою об’єкта *mutable. Ми також обговорили модулі ctypes, які можуть визначати покажчик C у Python. Ми визначили кілька чудових способів імітації покажчика в Python.