Моделирование указателей в 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, если кто-то может предложить другой язык, который может быть более подходящим.
Ответы
Ответ 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
Ответ 2
Вы можете прочитать Семантика имен переменных Python с точки зрения С++. Нижняя строка: Все переменные являются ссылками.
Более того, не думайте в терминах переменных, а в терминах объектов, которые можно назвать.
Ответ 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), поэтому, пожалуйста, не ненавидите меня, если я ошибаюсь. Его просто моя интуиция, которая позволила мне так думать, так что не ненавидите меня!