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é.
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
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é.
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.
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!
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
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.
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 ...
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
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!