Имитация указателей в Python - PullRequest
52 голосов
/ 18 июля 2009

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

Одной из функций ihl являются указатели и ссылки, которые ведут себя так, как вы ожидаете от C или 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 не самый лучший, но до сих пор мои исследования не дали ничего обещающего: (

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

Ответы [ 10 ]

78 голосов
/ 18 июля 2009

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

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
20 голосов
/ 18 июля 2009

Возможно, вы захотите прочитать Семантика имен переменных Python с точки зрения C ++ .Нижняя строка: Все переменные являются ссылками .

Точнее, не в терминах переменных, а в терминах объектов, которые могут быть названы .

14 голосов
/ 18 июля 2009

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

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

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

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

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

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

(Я сделал это, чтобы обойти отсутствие Python в «нелокальном» в 2.x несколько раз.)

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

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

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

9 голосов
/ 22 декабря 2012

Почти так же, как ephemient answer , за который я проголосовал, вы можете использовать встроенную в Python функцию property . Он будет делать что-то похожее на класс ref в ответе ephemient, за исключением того, что теперь вместо принудительного использования методов 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. ума!

4 голосов
/ 18 июля 2009

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

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

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

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

>>> id(b)
28354600
3 голосов
/ 18 июля 2009

Все в 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]

«Разыменование» не имеет смысла, так как это все ссылки. Больше ничего нет, так что нечего разыскивать.

1 голос
/ 18 июля 2009

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

# 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

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

0 голосов
/ 06 июля 2015

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

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

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), поэтому, пожалуйста, не ненавидите меня, если я ошибаюсь. Это просто моя интуиция, которая позволяет мне так думать, поэтому не ненавидь меня!

0 голосов
/ 18 ноября 2014
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
0 голосов
/ 18 июля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...