В некоторых простых приложениях, которые мне приходилось писать на C/C++, я видел легкость, с которой определенные задачи решаются с помощью указателей . Теперь больше интересует другой язык: Python, я заметил отсутствие этого понятия. С чем связано это отсутствие? Так как Python — очень мощный и часто используемый язык, то какая концепция заменяет его?Подразумевается ли он в типах данных, в присваиваниях, в создании экземпляра класса?
Чрезвычайно простым примером может быть то, что на C мы можем написать что-то вроде этого:
#include <stdio.h>
int main(void) {
// your code goes here
int a = 5;
int *b = &a;
printf("a = %d; b = %d\n", a, *b); // (1)
a = 6;
printf("a = %d; b = %d\n", a, *b); // (2)
return 0;
}
(1): а = 5; б = 5
(2): а = 6; б = 6
b
указывает на адрес памяти a
, любые изменения a
которого можно наблюдать путем разыменования b
. Любое косвенное присвоение *b = <valor>;
изменит a
.
Но в Питоне:
a = 5
b = a
print "a = {0:d}; b = {1:d}".format(a,b) # (1)
b is a # resultado: True
a = 6
print "a = {0:d}; b = {1:d}".format(a,b) # (2)
b is a # resultado: False
(1): а = 5; б = 5
(2): а = 6; б = 5
В начале a
и b
относятся к одному и тому же объекту. Затем, когда a
он изменяется, создается новый объект; поэтому оба относятся к разным объектам и разным значениям.
Невозможно сделать с этим типом данных то, что C делает в Python, но можно сделать что-то подобное с изменяемыми типами данных; однако это возможно только тогда, когда мы делаем внутренние модификации изменяемых данных, например: меняем значение элемента списка.
a = [1, 2]
b = a
print b is a # resultado: True
b.append(3)
print b is a # resultado: True
a = [4, 5]
print b is a # resultado: False
Его отсутствие связано с тем, что явное использование указателей характерно для языков более низкого уровня, таких как C. Языки высокого уровня, такие как Python, избегают его, чтобы сделать его использование более простым и гибким, поскольку а также не нужно знать подробности модели данных.
Тот факт, что программисту Python не приходится иметь дело с указателями, не означает, что интерпретатор не использует их. На самом деле, он использует их имплицитно обильно.
В Python все является объектом, созданным в динамической (автоматически поддерживаемой) памяти. Когда вы вызываете функцию, аргументы передаются через их указатели. Это то, что известно как соглашение о вызове объекта. Точно так же, если вы назначаете
a = b
,a
он сохраняет указатель на файлb
. Таким образом, все переменные являются указателями на объекты, которые обрабатываются неявно.Нам нужно различать неизменяемые и изменяемые объекты.
x = 2015
создаст весь объект и укажетx
на него, но содержимое этого объекта нельзя будет изменить. Если вы затем назначитеx = 2016
, внутри будет создан новый объект с новым содержимым.v = [1]
а затем вызываетеv.append(2)
,v
он по-прежнему будет указывать на тот же объект, но его содержимое изменится.Короче говоря, при запуске этого кода:
Результат будет:
В C указатели обычно удовлетворяют трем требованиям: относятся к динамически зарезервированным структурам, передают параметры в функцию по ссылке или выполняют итерацию по коллекции.
В случае Python и объектных языков с собственной памятью в целом переменные выполняют функцию ссылки на динамически создаваемые структуры: можно создавать экземпляры объектов в любое время.
В общем случае объекты хранятся в динамической памяти процессов, а переменные — это ссылки на них: почти почти ссылки — это абстракции указателей, с еще некоторыми свойствами.
По этой причине передача параметров всегда осуществляется по ссылке, поэтому для этого не нужны никакие указатели.
Наконец, в объектных языках есть объекты-итераторы, которые предоставляют высокоуровневый интерфейс для обхода коллекций данных.
Абстрагирование от деталей памяти процесса — это то, что востребовано в языках, и именно поэтому указатели не нужны: по замыслу .
Ответ @GuillermoRuiz кажется мне превосходным, но я хотел бы углубиться в некоторые подробности об изменчивости и неизменности, которые поначалу часто сбивают с толку, но которые очень понятны, если вы помните, что все они являются указателями .
Изменить элементы в списке
Тот факт, что список является « изменяемым », означает, что мы можем не только добавлять в него элементы, но и изменять сохраненные значения.
На самом деле, будучи пуристами, список содержит только «указатели» на рассматриваемые данные. То есть такой список:
На самом деле он содержит три указателя, каждый из которых указывает на целое число соответствующих значений
1
и2
.3
Если мы теперь изменим первый элемент:При печати списка мы увидим:
Это не означает , что первый элемент списка был заменен на 100 (что было бы верно в массиве C), но что был создан новый объект типа integer и что указатель, который был в первом элементе списка список list, который указывал на
1
, теперь указывает на100
. Предыдущий1
становится «разыменованным» и позже будет удален из памяти сборщиком мусора.Копии списков
С другой стороны, тот факт, что переменные на самом деле являются указателями (или, если хотите, ссылками), подразумевает следующее присваивание:
не копирует элементы
a
, а просто присваиваетb
копию указателя.a
. То естьa
иb
фактически указывают на один и тот же список. Итак, если мы делаем:это то же самое, как если бы мы сделали
a[0] = 50
.Чтобы проверить, «указывают» ли две переменные на одни и те же данные, Python предлагает компаратор
is
:Если мы не хотим, чтобы они указывали на один и тот же, а были копией (в разных местах памяти), мы можем добиться этого следующим образом:
или таким образом:
В любом из этих двух случаев:
Оператор
==
сравнивает элементыa
с элементамиb
,is
сравнивая идентичность с идентичностью ,a
чтоb
на практике состоит в сравнении того, к какому адресу памяти относится каждый из них.Списки как параметры
Вышеприведенное также объясняет, почему функция может изменять элементы списка, который она получает в качестве параметра:
В кортеже, будучи неизменяемым, это невозможно сделать.
Неизменность, но не более
Но будьте осторожны , тот факт, что кортеж является неизменяемым, означает только то, что значение не может быть изменено
tupla[i]
на другое значение, но если бы этоtupla[i]
был список, значения этого списка могли бы быть изменены:Это немного хак, но если вам действительно нужна 1 переменная и «указатель», вы можете сделать:
Я думаю, что это не нужно, в любом случае, если кто-то знает лучший способ, дайте мне знать! :D