web-dev-qa-db-fra.com

Simulation de pointeurs dans Python

J'essaie de compiler un langage interne (ihl) en Python.

L'une des fonctionnalités ihl est des pointeurs et des références qui se comportent comme vous pouvez vous attendre de C ou C++.

Par exemple, vous pouvez faire ceci:

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

Existe-t-il un moyen de dupliquer cette fonctionnalité en Python.

Je dois souligner que je pense que j'ai confondu quelques personnes. Je ne veux pas de pointeurs en Python. Je voulais juste avoir une idée des experts Python là-bas) , que Python je devrais générer pour simuler le cas que j'ai montré ci-dessus

Mon Python n'est pas le meilleur mais jusqu'à présent, mon exploration n'a rien donné de prometteur :(

Je dois souligner que nous cherchons à passer de notre langage ihl à un langage plus commun afin que nous ne soyons pas vraiment liés à Python si quelqu'un peut suggérer un autre langage qui pourrait être plus approprié.

53
chollida

Cela peut être fait explicitement.

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
78
ephemient

Vous voudrez peut-être lire Sémantique de Python noms de variables dans une perspective C++ . La ligne du bas: Toutes les variables sont des références .

Plus précisément, ne pensez pas en termes de variables, mais en termes d'objets qui peuvent être nommé.

20
Stephan202

Si vous compilez un langage de type C, dites:

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

en Python, alors tout le "tout en Python est une référence" est un terme impropre.

Il est vrai que tout dans Python est une référence, mais le fait que de nombreux types de noyau (entiers, chaînes) soient immuables annule effectivement cela dans de nombreux cas. Il n'y a pas direct façon d'implémenter ce qui précède en Python.

Maintenant, vous pouvez le faire indirectement: pour tout type immuable, enveloppez-le dans un type mutable. La solution d'Ephemient fonctionne, mais je fais souvent ceci:

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

(J'ai fait cela pour contourner le manque de "non local" de Python en 2.x à quelques reprises.)

Cela implique beaucoup plus de surcharge: chaque type immuable (ou chaque type, si vous n'essayez pas de distinguer) crée soudainement une liste (ou un autre objet conteneur), vous augmentez donc considérablement la surcharge pour les variables. Individuellement, ce n'est pas beaucoup, mais cela s'ajoutera lorsqu'il sera appliqué à une base de code entière.

Vous pouvez réduire cela en encapsulant uniquement les types immuables, mais vous devrez ensuite garder une trace des variables dans la sortie qui sont encapsulées et de celles qui ne le sont pas, afin que vous puissiez accéder à la valeur avec "a" ou "a [0]" de manière appropriée. Ça deviendra probablement poilu.

Quant à savoir si c'est une bonne idée ou non - cela dépend de la raison pour laquelle vous le faites. Si vous voulez juste que quelque chose fasse tourner une VM, j'aurais tendance à dire non. Si vous voulez pouvoir appeler votre langue existante à partir de Python, je vous suggère de prendre votre VM et de créer Python liaisons pour cela, donc vous peut y accéder et l'appeler depuis Python.

14
Glenn Maynard

Presque exactement comme éphémèreréponse , que j'ai voté, vous pouvez utiliser la fonction intégrée propriété de Python. Il fera quelque chose de presque similaire à la classe ref dans la réponse de l'éphémient, sauf maintenant, au lieu d'être forcé d'utiliser les méthodes get et set pour accéder à un ref instance, vous appelez simplement les attributs de votre instance que vous avez affectés en tant que propriétés dans la définition de classe. De Python docs (sauf que j'ai changé [~ # ~] c [~ # ~] en 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.")

Les deux méthodes fonctionnent comme un pointeur C, sans recourir à global. Par exemple, si vous avez une fonction qui prend un pointeur:

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

Par exemple

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

Une autre option totalement folle serait d'utiliser ctypes de Python. Essaye ça:

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

ou si vous voulez devenir vraiment chic

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

ce qui ressemble plus à la demande originale d'OP. fou!

9
Mark Mikofski

Comme d'autres l'ont dit ici, toutes les variables Python sont essentiellement des pointeurs.

La clé pour comprendre cela d'un point de vue C est d'utiliser la fonction unknown by many id (). Il vous indique à quelle adresse la variable pointe.

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

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

>>> id(b)
28354600
4
Unknown

Tout en Python est déjà des pointeurs, mais ça s'appelle des "références" en Python. C'est la traduction de votre code en 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]

"Déréférencer" n'a aucun sens, car il s'agit de toutes références. Il n'y a rien d'autre, donc rien à redire.

3
Lennart Regebro

C'est maladroit, mais une pensée ...

# 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

Mais il n'y aurait pas d'arithmétique de pointeur ou autre, et on ne dirait pas quels autres problèmes vous pourriez rencontrer ...

1
Anon
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
Jeremy Banks

Je pense que cet exemple est court et clair.

Ici, nous avons une classe avec une liste implicite:

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

En regardant ce profil de mémoire (en utilisant: from memory_profiler import profile), mon intuition me dit que cela peut en quelque sorte simuler des pointeurs comme en 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]

Et ici, nous avons un moi explicite en classe:

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: je suis un programme d'auto-apprentissage (commencé avec Python), alors ne me détestez pas si je me trompe. C'est juste mon intuition, qui me laisse penser comme ça, alors ne me déteste pas!

0
user1839053