Подтвердить что ты не робот

Моделирование указателей в Python

Я пытаюсь перекрестно скомпилировать собственный язык (ihl) на Python.

Одна из функций ihl - указатели и ссылки, которые ведут себя так, как вы ожидали бы от C или С++.

Например, вы можете сделать это:

a = [1,2];  // a has an array 
b = &a;     // b points to a
*b = 2;     // derefernce b to store 2 in a
print(a);   // outputs 2
print(*b);   // outputs 2

Есть ли способ дублировать эту функциональность в Python.

Я должен указать, что я думаю, что я смутил несколько человек. Мне не нужны указатели на Python. Я просто хотел получить представление от экспертов Python, что я должен создать Python для моделирования случая, который я показал выше

Мой Python не самый большой, но до сих пор мое исследование не принесло ничего обещающего: (

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

4b9b3361

Ответ 1

Это можно сделать явно.

class ref:
    def __init__(self, obj): self.obj = obj
    def get(self):    return self.obj
    def set(self, obj):      self.obj = obj

a = ref([1, 2])
b = a
print a.get()  # => [1, 2]
print b.get()  # => [1, 2]

b.set(2)
print a.get()  # => 2
print b.get()  # => 2

Ответ 3

Если вы компилируете C-подобный язык, скажите:

func()
{
    var a = 1;
    var *b = &a;
    *b = 2;
    assert(a == 2);
}

в Python, тогда все "все в Python является ссылкой" - это неправильное название.

Это правда, что все в Python является ссылкой, но тот факт, что многие основные типы (ints, strings) неизменяемы, эффективно отменяет это для многих случаев. Нет прямого способа реализовать выше в Python.

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

a = [1]
b = a
b[0] = 2
assert a[0] == 2

(Я сделал это для того, чтобы работать с нехваткой Python "нелокальных" в 2.x несколько раз.)

Это подразумевает намного больше накладных расходов: каждый неизменный тип (или каждый тип, если вы не пытаетесь отличить), внезапно создает список (или другой объект-контейнер), поэтому вы значительно увеличиваете накладные расходы для переменных. Индивидуально, это не так много, но оно будет складываться при применении ко всей базы кода.

Вы можете уменьшить это, только обертывая неизменяемые типы, но тогда вам нужно будет отслеживать, какие переменные на выходе завернуты, а какие нет, поэтому вы можете получить доступ к значению с помощью "a" или "a [ 0]" соответственно. Вероятно, он будет волосатым.

Что касается хорошей идеи или нет - это зависит от того, почему вы это делаете. Если вы просто хотите что-то запустить VM, я бы сказал, что нет. Если вы хотите иметь возможность звонить на ваш существующий язык с Python, я бы предложил взять вашу существующую виртуальную машину и создать для нее привязки Python, чтобы вы могли получить доступ к ней и вызвать ее из Python.

Ответ 4

Почти как ephemient answer, который я проголосовал, вы можете использовать встроенный Python property. Он будет делать что-то почти похожее на класс ref в эфемерном ответе, за исключением теперь, вместо того, чтобы принудительно использовать методы get и set для доступа к экземпляру ref, вы просто вызываете атрибуты своего экземпляра, вы присвоили свойства в определении класса. Из документов Python (за исключением того, что я изменил C на ptr):

class ptr(object):
    def __init__(self):
        self._x = None
    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

Оба метода работают как указатель C, не прибегая к global. Например, если у вас есть функция, которая принимает указатель:

def do_stuff_with_pointer(pointer, property, value):
    setattr(pointer, property, value)

Например

a_ref = ptr()      # make pointer
a_ref.x = [1, 2]   # a_ref pointer has an array [1, 2]
b_ref = a_ref      # b_ref points to a_ref
# pass ``ptr`` instance to function that changes its content
do_stuff_with_pointer(b_ref, 'x', 3)
print a_ref.x      # outputs 3
print b_ref.x      # outputs 3

Другим и совершенно безумным вариантом было бы использовать Python ctypes. Попробуйте следующее:

from ctypes import *
a = py_object([1,2]) # a has an array 
b = a                # b points to a
b.value = 2          # derefernce b to store 2 in a
print a.value        # outputs 2
print b.value        # outputs 2

или если вы хотите получить действительно фантастический

from ctypes import *
a = py_object([1,2])   # a has an array 
b = pointer(a)         # b points to a
b.contents.value = 2   # derefernce b to store 2 in a
print a.value          # outputs 2
print b.contents.value # outputs 2

который больше похож на исходный запрос OP. сумасшедший!

Ответ 5

Все в Python уже есть указатели, но он называется "ссылки" в Python. Это перевод вашего кода на Python:

a = [1,2]  // a has an array 
b = a     // b points to a
a = 2      // store 2 in a.
print(a)   // outputs 2
print(b)  // outputs [1,2]

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

Ответ 6

Как говорили другие, все переменные Python являются, по существу, указателями.

Ключом к пониманию этого с точки зрения C является использование неизвестного многими функциями id(). Он сообщает вам, на какой адрес указывает переменная.

>>> a = [1,2]
>>> id(a)
28354600

>>> b = a
>>> id(a)
28354600

>>> id(b)
28354600

Ответ 7

Это глупо, но мысль...

# Change operations like:
b = &a

# To:
b = "a"

# And change operations like:
*b = 2

# To:
locals()[b] = 2


>>> a = [1,2]
>>> b = "a"
>>> locals()[b] = 2
>>> print(a)
2
>>> print(locals()[b])
2

Но не было бы арифметики указателей или таковой, и не было бы никаких других проблем, с которыми вы могли бы столкнуться...

Ответ 8

Отрицательный, без указателей. Вы не должны нуждаться в них с тем, как язык разработан. Однако я слышал неприятный слух о том, что вы можете использовать модуль ctypes, чтобы использовать их. Я не использовал его, но он пахнет мне.

Ответ 9

class Pointer(object):
    def __init__(self, target=None):
        self.target = target

    _noarg = object()

    def __call__(self, target=_noarg):
        if target is not self._noarg:
            self.target = target
        return self.target
a = Pointer([1, 2])
b = a

print a() # => [1, 2]
print b() # => [1, 2]

b(2)
print a()  # => 2
print b()  # => 2

Ответ 10

Я думаю, что этот пример короткий и ясный.

Здесь у нас есть класс с неявным списком:

class A: 
   foo = []
a, b = A(), A()
a.foo.append(5)
b.foo
ans: [5]

Глядя на этот профиль памяти (используя: from memory_profiler import profile), моя интуиция подсказывает мне, что это может каким-то образом имитировать указатели, как в C:

Filename: F:/MegaSync/Desktop/python_simulate_pointer_with_class.py

Line #    Mem usage    Increment   Line Contents
================================================
     7     31.2 MiB      0.0 MiB   @profile
     8                             def f():
     9     31.2 MiB      0.0 MiB       a, b = A(), A()
    10                                 #here memoery increase and is coupled
    11     50.3 MiB     19.1 MiB       a.foo.append(np.arange(5000000))
    12     73.2 MiB     22.9 MiB       b.foo.append(np.arange(6000000))
    13     73.2 MiB      0.0 MiB       return a,b


[array([      0,       1,       2, ..., 4999997, 4999998, 4999999]), array([      0,       1,       2, ..., 5999997, 5999998, 5999999])] [array([      0,       1,       2, ..., 4999997, 4999998, 4999999]), array([      0,       1,       2, ..., 5999997, 5999998, 5999999])]
Filename: F:/MegaSync/Desktop/python_simulate_pointer_with_class.py

Line #    Mem usage    Increment   Line Contents
================================================
    14     73.4 MiB      0.0 MiB   @profile
    15                             def g():
    16                                 #clearing b.foo list clears a.foo
    17     31.5 MiB    -42.0 MiB       b.foo.clear()
    18     31.5 MiB      0.0 MiB       return a,b


[] []
Filename: F:/MegaSync/Desktop/python_simulate_pointer_with_class.py

Line #    Mem usage    Increment   Line Contents
================================================
    19     31.5 MiB      0.0 MiB   @profile
    20                             def h():
    21                                 #and here mem. coupling is lost ;/
    22     69.6 MiB     38.1 MiB       b.foo=np.arange(10000000)
    23                                 #memory inc. when b.foo is replaced
    24    107.8 MiB     38.1 MiB       a.foo.append(np.arange(10000000))
    25                                 #so its seams that modyfing items of
    26                                 #existing object of variable a.foo,
    27                                 #changes automaticcly items of b.foo
    28                                 #and vice versa,but changing object
    29                                 #a.foo itself splits with b.foo
    30    107.8 MiB      0.0 MiB       return b,a


[array([      0,       1,       2, ..., 9999997, 9999998, 9999999])] [      0       1       2 ..., 9999997 9999998 9999999]

И здесь мы имеем явное я в классе:

class A: 
    def __init__(self): 
        self.foo = []
a, b = A(), A()
a.foo.append(5)
b.foo
ans: []

Filename: F:/MegaSync/Desktop/python_simulate_pointer_with_class.py

Line #    Mem usage    Increment   Line Contents
================================================
    44    107.8 MiB      0.0 MiB   @profile
    45                             def f():
    46    107.8 MiB      0.0 MiB       a, b = B(), B()
    47                                 #here some memory increase
    48                                 #and this mem. is not coupled
    49    126.8 MiB     19.1 MiB       a.foo.append(np.arange(5000000))
    50    149.7 MiB     22.9 MiB       b.foo.append(np.arange(6000000))
    51    149.7 MiB      0.0 MiB       return a,b


[array([      0,       1,       2, ..., 5999997, 5999998, 5999999])] [array([      0,       1,       2, ..., 4999997, 4999998, 4999999])]
Filename: F:/MegaSync/Desktop/python_simulate_pointer_with_class.py

Line #    Mem usage    Increment   Line Contents
================================================
    52    111.6 MiB      0.0 MiB   @profile
    53                             def g():
    54                                 #clearing b.foo list
    55                                 #do not clear a.foo
    56     92.5 MiB    -19.1 MiB       b.foo.clear()
    57     92.5 MiB      0.0 MiB       return a,b


[] [array([      0,       1,       2, ..., 5999997, 5999998, 5999999])]
Filename: F:/MegaSync/Desktop/python_simulate_pointer_with_class.py

Line #    Mem usage    Increment   Line Contents
================================================
    58     92.5 MiB      0.0 MiB   @profile
    59                             def h():
    60                                 #and here memory increse again ;/
    61    107.8 MiB     15.3 MiB       b.foo=np.arange(10000000)
    62                                 #memory inc. when b.foo is replaced
    63    145.9 MiB     38.1 MiB       a.foo.append(np.arange(10000000))
    64    145.9 MiB      0.0 MiB       return b,a


[array([      0,       1,       2, ..., 9999997, 9999998, 9999999])] [      0       1       2 ..., 9999997 9999998 9999999]

ps: Я сам программирую программирование (начинаюсь с Python), поэтому, пожалуйста, не ненавидите меня, если я ошибаюсь. Его просто моя интуиция, которая позволила мне так думать, так что не ненавидите меня!