2009-07-17 27 views
51

Sto tentando di compilare un linguaggio interno (ihl) in Python.Simulazione di puntatori in Python

Una delle funzionalità di ihl è puntatori e riferimenti che si comportano come ci si aspetterebbe da C o C++.

Per esempio si può fare questo:

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 

C'è un modo per duplicare questa funzionalità in Python.

Vorrei sottolineare che penso di aver confuso alcune persone. Non voglio i puntatori in Python. Volevo solo avere un senso dagli esperti di Python là fuori, cosa dovrei generare in Python per simulare il caso che ho mostrato sopra

My Python non è il massimo ma finora la mia esplorazione non ha prodotto nulla promettente :(

Tengo a precisare che stiamo cercando di passare dalla nostra DIU a un linguaggio più comune, così non siamo davvero legati a Python se qualcuno può suggerire un altro linguaggio che può essere più adatto.

+0

Quindi stai provando a compilare un linguaggio abbastanza di basso livello con uno abbastanza di alto livello? Hai considerato altre piattaforme per la tua VM? –

+3

http://cluecc.sourceforge.net compila C in vari linguaggi di alto livello, ma non ha ancora un backend Python. Sarebbe interessante vedere come funziona, però :) – ephemient

+0

Un problema con il tuo esempio è che stai usando interi, che in Python è immutabile. Io, non puoi cambiarli. Questo, combinato con il fatto che hai una variabile array, che tu sovrascrivi con un intero nel tuo codice C (che è orribile da una prospettiva C) significa che stai chiedendo un modo per abusare di Python in un modo simile a come tu uso improprio C. Questo non ha alcun senso. –

risposta

76

Questo può essere fatto in modo esplicito.

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 
+5

Potresti non voler usare il nome "ref", poiché si tratta dello stesso nome del riferimento weakref. Forse "ptr" o qualcosa del genere. Un'implementazione ragionevole, però. –

+0

Stavo pensando a SML, dove questo è chiamato ''a ref', ma sì, sarebbe meglio scegliere un nome più unico. Non sono sicuro che 'ptr' abbia tutto ciò che ha molto senso, però; non è in realtà un puntatore, è più simile a un singolo contenitore ... – ephemient

+2

Si noti che questo è anche solo significativo quando si usano oggetti immutabili come intarsi o stringhe. Per oggetti mutabili a = Something(); b = a; è perfettamente E anche con oggetti immutabili è praticamente inutile ... –

0

Negativo, senza indicazioni. Non dovresti averne bisogno con il modo in cui è progettato il linguaggio, tuttavia ho sentito una brutta voce che potresti usare il modulo: ctypes per usarli. non l'ho usato, ma ha un cattivo odore per me.

+0

So che non ci sono puntatori in Python :) Il mio obiettivo è quello di compilare il mio linguaggio esistente in Python in modo da non dover più supportare un runtime. Quale Python dovrei generare per simulare il mio esempio sopra? – chollida

20

Si consiglia di leggere Semantics of Python variable names from a C++ perspective. La riga inferiore: Tutte le variabili sono riferimenti.

Più precisamente, non pensare in termini di variabili, ma in termini di oggetti che possono essere denominati.

+3

Ovviamente ha variabili; una variabile in Python è un oggetto con nome. Dire "Python non ha variabili" è solo confondere le cose inutilmente. –

+0

@Glenn: Prendo variabile per indicare una 'posizione di memoria denominata'. Certo, potrebbe non essere la definizione corretta. Anche se questa frase su Wikipedia, se la interpreto correttamente, sembra essere d'accordo con me: http://en.wikipedia.org/wiki/Variable_%28programming%29#In_source_code – Stephan202

+0

Non se stai pensando alle variabili come fisse nella memoria spazio, che è quello che è in C. –

4

Tutto in Python è già puntatori, ma è chiamato "riferimenti" in Python. Questa è la traduzione del codice per 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] 

"Dereferenziare" non ha senso, in quanto è tutti riferimenti. Non c'è nient'altro, quindi niente da dereferenziare.

+0

Grazie per la risposta. Ciò che mi riguarda è l'ultima riga della tua risposta. Per la semantica corretta nella mia vecchia lingua avrei bisogno di essere ora di stampare 2 come nella vecchia lingua è un puntatore a. – chollida

+1

Questo non è un puntatore quindi, ma un alias, in cui si punta un nome a un altro nome. Non è possibile farlo in Python, poiché è completamente inutile. In Python lo chiameresti semplicemente "un" per tutto il tempo. Non sono necessari alias. –

4

Come altri hanno già detto, tutte le variabili Python sono essenzialmente dei puntatori.

La chiave per comprendere questo da una prospettiva C è utilizzare lo sconosciuto da molte funzioni id(). Ti dice a quale indirizzo la variabile punta.

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

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

>>> id(b) 
28354600 
+3

Il problema è come dereferire tale indirizzo. – brannerchinese

+1

è ancora molto utile per identificare gli oggetti.senza questo, avresti bisogno di hash il contenuto o assegnare esplicitamente un id. in C, i puntatori vengono talvolta utilizzati per distinguere gli oggetti – UXkQEZ7

+0

"Dettaglio implementazione CPython: questo è l'indirizzo dell'oggetto in memoria". https://docs.python.org/2/library/functions.html#id – UXkQEZ7

1

Questo è goofy, ma un pensiero ...

# 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 

Ma non ci sarebbe aritmetica puntatore o tali, e può dire quali altri problemi si potrebbe incorrere in ...

+0

Questa probabilmente non è l'idea più grande, perché le modifiche ai locali () non sono garantiti per essere riflessi nell'ambiente. –

+0

Inoltre, non è possibile passare 'b' ad altre funzioni in questo modo. – ephemient

+0

Come per il commento di Paul - l'ho testato in una funzione, e non ha nemmeno cambiato la variabile in là. Può cambiare globalmente in questo modo in una funzione, ma i locals() a quanto pare stanno solo dando una copia del dict quando sono in una funzione. – Anon

12

Se si compila un linguaggio simile al C, dicono:

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

in Python, quindi tutti i "tutto in Python è un riferimento" roba è un termine improprio.

È vero che tutto in Python è un riferimento, ma il fatto che molti tipi di core (int, stringhe) siano immutabili annulla efficacemente questo per molti casi. Non esiste un modo diretto per implementare quanto sopra in Python.

Ora, puoi farlo indirettamente: per qualsiasi tipo immutabile, avvolgilo in un tipo mutevole. La soluzione di Ephemient funziona, ma spesso mi basta fare questo: (. Ho fatto questo per aggirare la mancanza di "non locale" di Python in 2.xa paio di volte)

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

Questo implica molto di più overhead: ogni tipo immutabile (o ogni tipo, se non si tenta di distinguere) crea improvvisamente una lista (o un altro oggetto contenitore), quindi si aumenta in modo significativo il sovraccarico delle variabili. Individualmente, non è molto, ma si sommerà se applicato a un'intera base di codice.

Si può ridurre questo solo avvolgendo tipi immutabili, ma poi è necessario tenere traccia di quali variabili nell'output sono state spostate e quali no, in modo da poter accedere al valore con "a" o "a [0] "appropriatamente. Probabilmente diventerà peloso.

Se questa è una buona idea o meno - questo dipende dal motivo per cui lo stai facendo. Se vuoi solo qualcosa per eseguire una VM, tenderei a dire di no. Se vuoi essere in grado di chiamare la tua lingua esistente da Python, ti suggerisco di prendere la tua macchina virtuale esistente e creare collegamenti Python per essa, così puoi accedere e chiamarci da Python.

+0

Sì, il mio 'ref' è fondamentalmente il caso degenerato di solo elemento di' list'. Un 1-'tuple 'sarebbe un overhead più basso, ma sfortunatamente quelli non sono mutabili. – ephemient

+1

A proposito, penso che quello che * vuoi * sia una cella - è ciò che Python usa per memorizzare chiusure. Sfortunatamente, questi sono dettagli di implementazione che non sono esposti - non puoi istanziarli direttamente. –

9

Quasi esattamente come ephemientanswer, che ho votato, è possibile utilizzare la funzione incorporata di Python property. Farà qualcosa di simile alla classe ref nella risposta di efemio, tranne che ora, invece di essere obbligati a usare i metodi get e set per accedere a un'istanza ref, basta chiamare gli attributi dell'istanza che hai assegnato come proprietà nella definizione della classe. Dalla documentazione Python (tranne ho cambiato C a 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.") 

Entrambi i metodi funzionano come un puntatore C, senza ricorrere a global. Per esempio, se si dispone di una funzione che prende un puntatore:

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

Per esempio

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 

Un altro, e totalmente folle opzione sarebbe quella di utilizzare Python di ctypes. Prova questo:

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 

o se si vuole ottenere davvero fantasia

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 

che è più simile richiesta originale del PO. pazzesco!

0
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

Penso che questo esempio è breve e chiaro.

Qui abbiamo classe con la lista implicita:

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

Guardando a questo profilo di memoria (utilizzando: from memory_profiler import profile), la mia intuizione mi dice che questo possa in qualche modo simulare i puntatori come in 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] 

E qui abbiamo sé esplicito in 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: io sono programmazione di autoapprendimento (iniziata con Python) quindi per favore non odiarmi se sbaglio. È solo una mia intuizione, che mi faccia pensare in questo modo, quindi non odiarmi!