- Указатели в Python подробно с практическими примерами
- Что такое указатель в Python?
- Почему Python не поддерживает указатели
- Объекты
- Неизменяемые и изменяемые объекты
- Понимание переменных Python
- Переменные в C
- Имена в Python
- Имитация указателей в Python
- Использование изменяемых типов в качестве указателя
- Использование объектов Python
- Модуль Python ctypes
- Заключение
Указатели в Python подробно с практическими примерами
В этом руководстве мы узнаем об указателях в Python и поймем, почему Python не поддерживает концепции указателя. Также выясним, как можно смоделировать указатели.
Что такое указатель в Python?
Указатель в Python – очень популярный и полезный инструмент для хранения адреса переменной. Если кто-то когда-либо работал с языком низкого уровня, таким как C, C ++, то, вероятно, были знакомы с указателями. Они очень эффективно управляют кодом. Для новичков это может быть немного сложно, но это одна из важных концепций программы. Однако, это может привести к различным ошибкам управления памятью. Таким образом, определение указателей:
«Указатели – это переменные, которые содержат адрес в памяти другой переменной. Переменные-указатели представлены звездочкой(*)».
Давайте посмотрим на следующий пример указателя на языке программирования C.
Пример – как использовать указатель в C
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».
Тип – слой CPython используется в качестве типа для обеспечения безопасности типов во время выполнения.
Наконец, есть значение, которое представляет собой фактическое значение, связанное с объектом.
Однако если мы углубимся в этот объект, мы обнаружим, что не все объекты одинаковы. Важное различие между типами объектов – неизменяемость и изменчивость. Прежде всего, нам нужно понять разницу между типами объекта, чтобы исследовать указатель в Python.
Неизменяемые и изменяемые объекты
Неизменяемые объекты не могут быть изменены, тогда как изменяемые объекты – могут. Давайте посмотрим на следующую таблицу общих типов и узнаем, являются ли они изменяемыми или нет.
Объекты | Тип |
---|---|
Int | Immutable |
Float | Immutable |
Bool | Immutable |
List | Mutable |
Set | Mutable |
Complex | Mutable |
Tuple | Immutable |
Frozenset | Immutable |
Dict | Mutable |
Мы можем проверить тип вышеуказанных объектов с помощью метода id(). Этот метод возвращает адрес памяти объекта.
Мы печатаем следующие строки в среде REPL.
В приведенном выше коде мы присвоили x значение 10. если мы изменим это значение с заменой, мы получим новые объекты.
Мы модифицируем приведенный выше код и получаем в ответ новые объекты.
s = "java" print(id(s)) s += "Tpoint" print(s) id(s)
2315970974512 JavaTpoint 1977728175088
Опять же, мы изменяем значение x, добавляя новую строку, и получаем новый адрес памяти. Попробуем добавить строку прямо в 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
Приведенный выше код возвращает ошибку, это означает, что строка не поддерживает мутацию. Итак, str – это неизменяемые объекты.
Теперь мы увидим изменяемый объект, такой как список.
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
Как мы видим в приведенном выше коде, my_list изначально имеет идентификатор, и мы добавили к списку цифру 5; my_list имеет тот же идентификатор, потому что список поддерживает изменчивость.
Понимание переменных Python
Способ определения переменных в Python сильно отличается от C или C ++. Переменная Python не определяет тип данных. На самом деле у Python есть имена, а не переменные.
Давайте разберемся, как переменная работает в C и как работает имя в Python.
Переменные в C
В языке C переменная заключается в том, что она содержит значение или сохраняет значение. Он определяется типом данных. Давайте посмотрим на следующий код, определяющий переменную.
- Выделите достаточно памяти для целого числа.
- Мы присваиваем этой ячейке памяти значение 286.
- X представляет это значение.
Если мы посмотрим на память –
Как мы видим, x имеет ячейку памяти для значения 286. Теперь мы присвоим x новое значение.
Это новое значение перезаписывает предыдущее значение. Это означает, что переменная x изменчива. Расположение значения x такое же, но значение изменилось. Это важный момент, указывающий на то, что x – это ячейка памяти, а не только ее имя.
Теперь мы вводим новую переменную, которая принимает x, затем y создает новый блок памяти.
Переменная y создает новое поле с именем y, копирует значение из x в поле.
Имена в Python
Как мы обсуждали ранее, в Python нет переменных. У него есть имена, и мы используем этот термин в качестве переменных. Но есть разница между переменными и именами. Посмотрим на следующий пример.
Приведенный выше код разбивается во время выполнения.
- Необходимо создать PyObject.
- Установите для PyObject тип integer.
- Установите значение 289 для PyObject.
- Создайте имя под названием x.
- Укажите x на новый PyObject.
- Увеличьте счетчик ссылок PyObject на 1.
Это будет выглядеть так, как показано ниже.
Мы можем понять внутреннюю работу переменной в Python. Переменная x указывает на ссылку на объект, и у него нет места в памяти, как раньше. Он также показывает, что x = 289 связывает имя x со ссылкой.
Теперь мы вводим новую переменную и присваиваем ей x.
В Python переменная y не создаст новый объект; это просто новое имя, указывающее на тот же объект. Refcount объекта также увеличился на единицу. Мы можем подтвердить это следующим образом.
Если мы увеличим значение y на единицу, оно больше не будет относиться к тому же объекту.
Это означает, что в Python мы не назначаем переменные. Вместо этого мы привязываем имена к ссылке.
Имитация указателей в Python
Python предоставляет альтернативные способы использования указателя. Эти два способа приведены ниже:
- использование изменяемых типов в качестве указателей;
- использование пользовательских объектов Python.
Давайте разберемся в данных способах.
Использование изменяемых типов в качестве указателя
В предыдущем разделе мы определили объекты изменяемого типа; мы можем рассматривать их, как если бы они были указателями для имитации поведения указателя. Давайте разберемся в следующем примере.
В приведенном выше коде мы определили указатель * a, а затем увеличили значение на единицу. Теперь мы реализуем это с помощью функции main().
Мы можем смоделировать этот тип поведения, используя изменяемый тип Python. Разберитесь в следующем примере.
def add_one(x): x[0] += 1 y = [2337] add_one(y) y[0]
Вышеупомянутая функция обращается к первому элементу списка и увеличивает его значение на единицу. Когда мы выполняем указанную выше программу, она печатает измененное значение y. Это означает, что мы можем реплицировать указатель с помощью изменяемого объекта. Но если мы попытаемся смоделировать указатель с помощью неизменяемого объекта.
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 = def car(): count["funcCalls"] += 1 def foo(): count["funCcalls"] += 1 car() foo() count["funcCalls"]
В приведенном выше примере мы использовали словарь count, который отслеживал количество вызовов функций. Когда вызывается функция foo(), счетчик увеличивается на 2, потому что dict изменяемый.
Использование объектов Python
В предыдущем примере мы использовали dict для имитации указателя в Python, но иногда становится трудно запомнить все используемые имена ключей. Мы можем использовать собственный класс Python вместо словаря. Давайте разберемся в следующем примере.
class Pointer(object): def __init__(self): self._metrics =
В приведенном выше коде мы определили класс 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"]
Мы использовали декоратор @property. Если вы не знакомы с декораторами, посетите наш учебник по декораторам 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, для которой требуется указатель. Давайте разберемся в следующем примере.
В приведенной выше функции мы увеличили значение x на единицу. Предположим, мы сохраняем указанный выше файл с именем incrPointer.c и вводим следующую команду в терминал.
$ gcc -c -Wall -Werror -fpic incrPointer.c $ gcc -shared -o libinc.so incrPointer.o
Первая команда компилирует incrPointer.c в объект с именем incrPointer.o. Вторая команда принимает объектный файл и создает libinic.so для взаимодействия с ctypes.
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)]
Если мы вызовем функцию с использованием другого типа, произойдет ошибка.
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 – переменная C. Ctypes предоставляет метод byref(), который используется для передачи ссылки на переменную.
Мы увеличили значение, используя ссылочную переменную.
Заключение
Мы обсудили, что указатель отсутствует в Python, но мы можем реализовать то же действие с * изменяемым объектом. Мы также обсудили модули ctypes, которые могут определять указатель C в Python. Мы определили несколько отличных способов имитации указателя.