1

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

Почти год программирую на 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", но требуется взять значение "А", которое в нее записано)

insolor
  • 49,104
  • 2
    В питоне нет указателей. Вы пытаетесь тащить понятия и подходы из C/С++ туда, где они вообще неприменимы. Из вашего вопроса я вообще не понял, какая у вас конечная задача, но я более чем уверен, что она решается совсем не так. – Xander Oct 04 '17 at 13:36
  • Задача записать в список элемент так, чтобы последующие изменения этого элемента можно было увидеть через элемент списка, в который я его записал. Теоретически это поможет оптимизировать код, т.к. мне не придется искать элементы, которые я записал, проверяя все элементы в списке, т.к. я смогу сразу обращаться к нему, количество элементов произвольное количество и зависит от предустановок – Анонимно Oct 04 '17 at 14:05
  • 1
    Ещё раз: вы не можете работать с питоном так, как привыкли в C/C++. Тут совершенно другие механизмы управления памятью, и вы не можете обращаться к адресам напрямую. – Xander Oct 04 '17 at 14:13
  • Возможно, есть другой способ решить данную проблему? – Анонимно Oct 04 '17 at 14:18
  • 1
    Не совсем понятна ваша проблема. Вы привели пример на Си, который можно повторить с помощью ctypes в python, а потом приводите какие-то примеры, которые делают совсем другое. – user207200 Oct 04 '17 at 14:29
  • Пишите на C/C++/Go. Питон специально создавался для того, чтобы можно было работать на более высоком уровне абстракции, и не лезть в память напрямую. – Xander Oct 04 '17 at 14:43
  • Получить адрес элемента можно методом 'id()' но в Python это абсолютно бессмысленное дело. – Игорь Игоряныч Oct 04 '17 at 15:16
  • mkkik, спасибо за предложенную библиотеку, к сожалению я не могу сказать подойдет ли данная библиотека, так как не работал с ней, возможность написать часть участков программы на С может подойти, я все еще надеюсь, что есть способ проще ) – Анонимно Oct 04 '17 at 15:32
  • Александр, у меня есть написанный код, с использованием библиотек Python, также я не изучал GO, и боюсь, что часть библиотек Python может отсутствовать – Анонимно Oct 04 '17 at 15:46
  • Игорь Игоряныч, находил несколько сайтов, в которых через id() получают элемент находящийся по адресу, с помощью сочетания функций: id(), ctypes.string_at(), struct.unpack_from привожу сайты: (https://habrahabr.ru/post/193890/) и (https://docs.python.org/2/library/ctypes.html"), к сожалению данный набор функций не вернул мне значение, как показано на приведенных выше сайтах, пока не понятно почему :(, собственно эти функции мне и нужны – Анонимно Oct 04 '17 at 16:03
  • 1
    @Анонимно, я подозреваю, что вам нужны не указатели, а какая-то абстрактная структура данных типа дерева или графа. Граф и дерево на Python можно реализовать, логику работы указателей для этого на Python переносить не нужно. – insolor Oct 04 '17 at 16:48
  • @insolor, вы абсолютно правы нужно дерево, точнее список-списков (в вопросе я старался сократить код до минимума), у меня не выходит нормально запомнить элемент в первой представленной функции на Python, в ней я пытаюсь записать объект under_element, но мне надо получить значение under_element после изменения значения на 'list' используя объект element, в примере после попытки запомнить under_element, значение даже не покидает функцию, не говоря об возможности в будущем его отслеживать, я нашел выход, плохой с моей точки зрения, представленный в последнем примере, ищу выход лучше приведенного – Анонимно Oct 04 '17 at 17:09
  • @Анонимно, объяснение в вопросе совсем не понятное. Сложностей типа "значение не покидает функцию" тоже не понял. Если передавать в функцию список, то ничто не мешает изменить этот список внутри функции, и это изменение будет видно снаружи. Если нужно "изменить" иммутабельный объект, то логично создавать измененную копию объекта, и возвращать ее через return (да и и с мутабельными объектами это вполне работает). – insolor Oct 04 '17 at 17:14
  • @insolor, в примере значение element не покидает функцию, так как он переходит из mutable в unmutable (так я понимаю происходящее в функции), хотя element (при повторном "погружении" в функцию) являлся элементом element, это первая проблема функции, вторая заключается в том, что я не могу отследить изменения under_element т.к. он unmutable, по этой логике обе проблемы решаются, если выйдет отслеживать изменения неизменяемого объекта (звучит странно согласен :)), возможно можно сделать копию, которая меняется вместе с тем объектом, который она скопировала, то есть A=B, а B=A всегда – Анонимно Oct 04 '17 at 17:38
  • **другими словами, как возможное решение, чтобы интерпретатор менял бы второй объект при изменении первого, на то чему равен первый – Анонимно Oct 04 '17 at 17:42

3 Answers3

3

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

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

Программируя на Питоне, старайтесь мыслить в терминах Питона. В Питоне есть имена и объекты. Контейнеры содержат ссылки на другие объекты. Никаких указателей нет. Data model.

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

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

В Питоне целые числа неизменяемы. К примеру, вы не можете поменять значение объекта, который представляет ноль. Присваивание целых (int) в Питоне

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


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

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

jfs
  • 52,361
1

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

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, что изначально неправильно. Нужно исходить наоборот от реальной задачи, потом переходить к абстрактным типам данных (графы, деревья и т.д.), которые в разных языках реализуются по-разному, и дальше уже реализовывать способом идиоматичным для выбранного языка.

insolor
  • 49,104
  • *можете показать или "намекнуть" как "передать имя переменной"? возможно может подойти, также буду рад, если вы скажете почему это плохой способ – Анонимно Oct 04 '17 at 18:03
  • *возможно я не совсем понимаю, что вы подразумеваете под передать имя переменной, а потом по этому имени обратиться к глобальной переменной – Анонимно Oct 04 '17 at 18:07
  • @Анонимно, плохой способ, потому что разыменование указателя в Си - быстрая операция, а обращение к элементу словаря (словаря глобальных переменных) по строковому ключу - не очень. Плюс это не универсальный способ: он будет работать с глобальными переменными, а с локальными переменными функций - нет. – insolor Oct 04 '17 at 18:09
  • Как сделать - попробуйте код abc = 'def'; globals()['abc'] = 'qwer'; print(abc). Т.е. можно в принципе передавать в функцию и записывать в список имя переменной 'under_element', а потом при выводе списка показывать значения переменных по их именам (если они попадутся в списке). Короче, левой рукой чешем правое ухо. – insolor Oct 04 '17 at 18:10
  • @Анонимно, ну, еще вариант - хранить в отдельном списке значения, а в функцию передавать их индексы, тогда получится что список со значениями - это как бы "массив" в памяти, а индекс - указатель на элемент в этом массиве. Тоже конечно странное какое-то решение будет. Лучше просто отказаться от идеи указателей на переменные, когда пишем на Python. – insolor Oct 04 '17 at 18:20
  • спасибо, постараюсь решить эту проблему :) – Анонимно Oct 04 '17 at 18:31
  • @Анонимно, добавил еще пример, может такой вариант подойдет. – insolor Oct 04 '17 at 18:46
0

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

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

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

vadim vaduxa
  • 8,897