Указатель/ссылка на область памяти

466 просмотра
0
0 Комментариев

Здравствуйте, постараюсь максимально подробно описать суть проблемы:

Почти год программирую на Python 3, потребовалось использовать указатель на переменную, которая может являться как изменяемой (mutable), например list, так и неизменяемой (immutable), например float.

Требуется используя адрес переменной «A» записать адрес в переменную «B», таким образом, чтобы после изменения «A», через переменную «B» можно было узнать значение в «A».

Пример на С/C++:

int A=0;
int *B;
B=&A;
printf("%d\n", *B); //ответ: 0
A=1;
printf("%d\n", *B); //ответ: 1

Пытаюсь реализовать на Python и написать функцию, которая принимает два элемента, например «element» и «under_element», где «element» типа список (list), «under_element» любой объект (float, str, list, «мой_объект»).

Функция записывает адрес объекта «under_element», в один из элементов списка «element» так, что при изменении «under_element» используя объект «element» можно было бы узнать актуальное значение «under_element»

Пример структуры функции на Python 3 («укороченный»):

def f(element, under_element, i=0):
    print("depth=", i, "\tfs_element=", element)
    if(i<1):
        f(element[0], under_element, i+1)
    else:
        element=under_element
    print("depth=", i, "\tff_element=", element)
 
element=[[0],[1]]
under_element='str'
print("start_element=", element, "\n")
 
f(element, under_element)
 
under_element='list'
print("\nfinish_element=", element)

Результат работы программы:

start_element= [[0], [1]]
 
depth= 0    fs_element= [[0], [1]]
depth= 1    fs_element= [0]
depth= 1    ff_element= str
depth= 0    ff_element= [[0], [1]]
 
finish_element= [[0], [1]]

Желаемый результат работы программы:

start_element= [[0], [1]]
 
depth= 0    fs_element= [[0], [1]]
depth= 1    fs_element= [0]
depth= 1    ff_element= str
depth= 0    ff_element= ['str', [1]]
 
finish_element= ['list', [1]]

Нашел решение состоящее из четырех пунктов:

  1. все элементы заключать в list, пример: under_element=['str']
  2. передавать в функцию переменные в виде лист
  3. обращаться к записываемому объекту без дополнительных [] (без указания глубины)
  4. обращаться к объекту, в который записываем с дополнительным [] (с указанием глубины)

Пример переделанной функции:

def f(element, under_element, i=0):
    print("depth=", i, "\tfs_element=", element)
    if(i<1):
        f(element[0], under_element, i+1)
    else:
        element[0]=under_element
    print("depth=", i, "\tff_element=", element)
 
element=[[0],[1]]
under_element=['str']
print("start_element=", element, "\n")
 
f(element, under_element)
 
under_element[0]='list'
print("\nfinish_element=", element)

Результат работы программы:

start_element= [[0], [1]]
 
depth= 0    fs_element= [[0], [1]]
depth= 1    fs_element= [0]
depth= 1    ff_element= [['str']]
depth= 0    ff_element= [[['str']], [1]]
 
finish_element= [[['list']], [1]]

Недостатки данного решения:

  1. мусор в массивах и коде программы
  2. нет гарантии, что пользователь функции пользуется переменными, как указано выше к данному примеру, и не передаст объект являющийся immutable (float, str и др.)

Повторюсь:

Требуется обратиться к объекту (1) через другой объект (2), например через элемент списка, или чтобы значение объекта (1) было в объект (2), даже после изменения объекта (1), другими словами всегда соответствовало значению объекта(1)

Пример:

A=0

запоминаем адрес в «B»

А=1

смотрим на «А» через «B» получаем 1

(после записи адреса «A» в «B», нельзя обратиться к «A», но требуется взять значение «А», которое в нее записано)


Добавить комментарий

3 Answers

Python Опубликовано 10.12.2018
0

Требуется используя адрес переменной «A» записать адрес в переменную «B», таким образом, чтобы после изменения «A», через переменную «B» можно было узнать значение в «A».

Не стоит пытаться использовать синтаксис Питона, чтобы писать на каком-то другом языке. Даже если вам удастся выразить чужие понятия, это скорее всего приведёт к неидиоматичному коду (сложному для понимания и неэффективному).
Программируя на Питоне, старайтесь мыслить в терминах Питона. В Питоне есть имена и объекты. Контейнеры содержат ссылки на другие объекты. Никаких указателей нет. Data model.

Пример на С/C++:

В Питоне целые числа неизменяемы. К примеру, вы не можете поменять значение объекта, который представляет ноль. Присваивание целых (int) в Питоне
A = 1 в Питоне не кладёт 1 в коробку с адресом A. В Питоне, A = 1 говорит, что к объекту 1 можно по имени A обращаться (ярлык прилепили): Python has «names» (на картинки посмотрите).

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

Вот здесь мы к действительной проблеме приближаемся. У вас есть некоторый алгоритм, который становится неэффективным, если его механически из Си на Питон перенести. То есть это XY-задача. Вместо попыток буквального переноса Си решения, стоит рассмотреть исходную задачу (которую Си код призван был решить) и выбрать более подходящее для Питона решение — не оглядываясь на реализации в Си. То есть необходимо привести изначальную задачу, которую вы пытались решить с помощью поиска элементов.

Добавить комментарий
0

Слегка модифицированный первый пример:

def f(element, under_element, i=0):
    print("depth=", i, "\tfs_element=", element)
    if(i<1):
        element[0] = f(element[0], under_element, i+1)
    else:
        element = under_element
    print("depth=", i, "\tff_element=", element)
    return element
 
element=[[0],[1]]
under_element='str'
print("start_element=", element, "\n")
 
f(element, under_element)
 
under_element='list'
print("\nfinish_element=", element)

Дает такой результат:

start_element= [[0], [1]]
 
depth= 0        fs_element= [[0], [1]]
depth= 1        fs_element= [0]
depth= 1        ff_element= str
depth= 0        ff_element= ['str', [1]]
 
finish_element= ['str', [1]]

Вот это:

under_element='str'
f(element, under_element)
under_element='list'  # хотим, чтобы 'str' внутри списка заменилось на 'list'

не сработает, потому что в функцию передается не указатель на переменную under_element (чего можно было бы добиться в Си), а значение переменной. В Python передать ссылку (или указатель) на переменную внутрь функции нет никакой возможности. Если очень захотеть, можно передать имя переменной, а потом по этому имени обратиться к глобальной переменной, но я не советовал бы так делать.

Как вариант, можно использовать «контейнерный» класс, в котором уже будет храниться значение, которое вы хотите изменять:

class Container:
    def __init__(self, item):
        self.item = item
 
    def __repr__(self):
        return 'Container(%r)' % self.item
 
    def __str__(self):
        return str(self.item)
 
 
def f(element, under_element, i=0):
    print("depth=", i, "\tfs_element=", element)
    if(i<1):
        element[0] = f(element[0], under_element, i+1)
    else:
        element = under_element
    print("depth=", i, "\tff_element=", element)
    return element
 
element=[[0],[1]]
under_element=Container('str')
 
print("start_element=", element, "\n")
 
f(element, under_element)
 
under_element.item='list'
print("\nfinish_element=", element)

Вывод:

start_element= [[0], [1]]
 
depth= 0        fs_element= [[0], [1]]
depth= 1        fs_element= [0]
depth= 1        ff_element= str
depth= 0        ff_element= [Container('str'), [1]]
 
finish_element= [Container('list'), [1]]

Если поменять реализацию метода __repr__ в классе Container:

class Container:
    def __init__(self, item):
        self.item = item
 
    def __repr__(self):
        return repr(self.item)
 
    def __str__(self):
        return str(self.item)

то формально вывод будет совпадать с требуемым:

start_element= [[0], [1]]
 
depth= 0        fs_element= [[0], [1]]
depth= 1        fs_element= [0]
depth= 1        ff_element= str
depth= 0        ff_element= ['str', [1]]
 
finish_element= ['list', [1]]

Но это не значит, что в списке хранится строка, просто класс при преобразовании в строку (например при выводе через print) будет «притворяться» строкой (точнее даже тем значением, которое лежит в поле .item, независимо от его типа). Но строковые операции с этим классом (сцепление со строкой и т.д.) не будут работать, нужно будет явно обращаться к полю .item объекта.


Еще добавлю — это все по сути попытка подтянуть низкоуровневые особенности С/С++ к Python, что изначально неправильно. Нужно исходить наоборот от реальной задачи, потом переходить к абстрактным типам данных (графы, деревья и т.д.), которые в разных языках реализуются по-разному, и дальше уже реализовывать способом идиоматичным для выбранного языка.

Добавить комментарий
0

Задача записать в список элемент так, чтобы последующие изменения этого элемента можно было увидеть через элемент списка, в который я его записал.

насколько я знаю это называется связный список, примеров в инете море

все остальное, что сзесь обсуждается это дичайшие велосипеды, или попытка програмирования на питоне, даже не попытавшись разобрятся как все устроено и питоне.

Добавить комментарий
Напишите свой ответ на данный вопрос.
Scroll Up