155 votes

Pointeurs en Python?

Je sais que Python n'a pas de pointeurs, mais y a-t-il un moyen d'obtenir ceci 2 à la place

>>> a = 1
>>> b = a # modifier cette ligne de quelque manière que ce soit pour que b "pointe vers" a
>>> a = 2
>>> b
1

?


Voici un exemple: je veux que form.data['field'] et form.field.value aient toujours la même valeur. Ce n'est pas complètement nécessaire, mais je pense que ce serait bien.


En PHP, par exemple, je peux faire ceci:

fields = $fields;
        foreach($this->fields as &$field) {
            $this->data[$field['id']] = &$field['value'];
        }
    }
}

$f = new Form([
    [
        'id' => 'fname',
        'value' => 'George'
    ],
    [
        'id' => 'lname',
        'value' => 'Lucas'
    ]
]);

echo $f->data['fname'], $f->fields[0]['value']; # George George
$f->data['fname'] = 'Ralph';
echo $f->data['fname'], $f->fields[0]['value']; # Ralph Ralph

Output:

GeorgeGeorgeRalphRalph

ideone


Ou comme ceci en C++ (Je pense que c'est correct, mais mon C++ est rouillé):

#include 
using namespace std;

int main() {
    int a;
    int* b = &a;
    *a = 1;
    cout << a << endl << *b << endl; # 1 1

    return 0;
}

30 votes

Peut-être que je peux poser une question similaire à celle de S.Lott (mais plus productive) : pouvez-vous nous montrer un vrai code où vous vouliez faire cela ? Peut-être même dans une autre langue qui correspond davantage à vos goûts ? Il est probable que le problème que vous essayez de résoudre se prête à une solution plus pythonique, et se concentrer sur "je veux des pointeurs" obscurcit la vraie réponse.

10 votes

Il ne faut pas beaucoup d'imagination; je peux penser à des dizaines de raisons pour vouloir le faire. Ce n'est tout simplement pas comme ça que cela se passe dans les langages sans pointeur comme Python; vous devez l'encadrer dans un conteneur qui n'est pas invariant, comme dans la réponse de Matt.

0 votes

@Ned : Question mise à jour. Un autre exemple serait pour une fonction, swap(a, b). Pas que j'en ai besoin tout de suite.

13voto

dan04 Points 33306

D'un point de vue, everything est un pointeur en Python. Votre exemple fonctionne beaucoup comme le code C++.

int* a = new int(1);
int* b = a;
a = new int(2);
cout << *b << endl;   // imprime 1

(Un équivalent plus proche utiliserait un type de `shared_ptr au lieu deint*`.)

Voici un exemple : je veux que form.data['field'] et form.field.value aient toujours la même valeur. Ce n'est pas complètement nécessaire, mais je pense que ce serait bien.

Vous pouvez faire cela en surchargeant __getitem__ dans la classe de form.data.``

0 votes

form.data n'est pas une classe. Est-il nécessaire de en faire une, ou puis-je la remplacer en vol ? (Ce n'est qu'un dictionnaire python) De plus, les données devraient avoir une référence à form pour accéder aux champs... ce qui rend la mise en œuvre de ceci peu esthétique.

6voto

Henry Points 341

Ceci est un pointeur python (différent de c / c++)

>>> a = lambda : print('Bonjour')
>>> a
 at 0x0000018D192B9DC0>
>>> id(a) == int(0x0000018D192B9DC0)
True
>>> from ctypes import cast, py_object
>>> cast(id(a), py_object).value == cast(int(0x0000018D192B9DC0), py_object).value
True
>>> cast(id(a), py_object).value
 at 0x0000018D192B9DC0>
>>> cast(id(a), py_object).value()
Bonjour

1voto

Leon Avery Points 756

J'ai écrit la classe simple suivante comme un moyen de pointer efficacement en python :

class Parameter:
    """Sucre syntaxique pour une paire getter/setter
    Usage:

    p = Parameter(getter, setter)

    Définir la valeur du paramètre :
    p(valeur)
    p.val = valeur
    p.set(valeur)

    Récupérer la valeur du paramètre :
    p()
    p.val
    p.get()
    """
    def __init__(self, getter, setter):
        """Créer un paramètre

        Paramètres positionnels requis :
        getter: appelé sans argument, récupère la valeur du paramètre.
        setter: appelé avec une valeur, définit le paramètre.
        """
        self._get = getter
        self._set = setter

    def __call__(self, val=None):
        if val is not None:
            self._set(val)
        return self._get()

    def get(self):
        return self._get()

    def set(self, val):
        self._set(val)

    @property
    def val(self):
        return self._get()

    @val.setter
    def val(self, val):
        self._set(val)

Voici un exemple d'utilisation (à partir d'une page du cahier jupyter) :

l1 = list(range(10))
def l1_5_getter(lst=l1, number=5):
    return lst[number]

def l1_5_setter(val, lst=l1, number=5):
    lst[number] = val

[
    l1_5_getter(),
    l1_5_setter(12),
    l1,
    l1_5_getter()
]

Out = [5, None, [0, 1, 2, 3, 4, 12, 6, 7, 8, 9], 12]

p = Parameter(l1_5_getter, l1_5_setter)

print([
    p(),
    p.get(),
    p.val,
    p(13),
    p(),
    p.set(14),
    p.get()
])
p.val = 15
print(p.val, l1)

[12, 12, 12, 13, 13, None, 14]
15 [0, 1, 2, 3, 4, 15, 6, 7, 8, 9]

Il est bien sûr également facile de faire fonctionner cela pour des éléments de dict ou des attributs d'un objet. Il existe même un moyen de faire ce que l'OP a demandé, en utilisant globals() :

def setter(val, dict=globals(), key='a'):
    dict[key] = val

def getter(dict=globals(), key='a'):
    return dict[key]

pa = Parameter(getter, setter)
pa(2)
print(a)
pa(3)
print(a)

Cela affichera 2, suivi de 3.

L'interaction avec l'espace de noms global de cette manière est en quelque sorte une idée terrible, mais cela montre qu'il est possible (bien qu'inconseillé) de faire ce que l'OP a demandé.

L'exemple est bien sûr assez inutile. Mais j'ai trouvé cette classe utile dans l'application pour laquelle je l'ai développée : un modèle mathématique dont le comportement est régi par de nombreux paramètres mathématiques définissables par l'utilisateur, de types divers (qui, étant donné qu'ils dépendent de arguments en ligne de commande, ne sont pas connus au moment de la compilation). Et une fois que l'accès à quelque chose a été encapsulé dans un objet Parameter, tous ces objets peuvent être manipulés de manière uniforme.

Bien que cela ne ressemble pas beaucoup à un pointeur C ou C++, cela résout un problème que j'aurais résolu avec des pointeurs si j'avais écrit en C++.

0voto

MikeTeX Points 437

Le code suivant émule exactement le comportement des pointeurs en C:

from collections import deque # plus efficace que la liste pour ajouter des éléments
pointer_storage = deque()
pointer_address = 0

class new:    
    def __init__(self):
        global pointer_storage    
        global pointer_address

        self.address = pointer_address
        self.val = None        
        pointer_storage.append(self)
        pointer_address += 1

def get_pointer(address):
    return pointer_storage[address]

def get_address(p):
    return p.address

null = new() # créer un pointeur nul, dont l'adresse est 0    

Voici des exemples d'utilisation:

p = new()
p.val = 'bonjour'
q = new()
q.val = p
r = new()
r.val = 33

p = get_pointer(3)
print(p.val, flush = True)
p.val = 43
print(get_pointer(3).val, flush = True)

Mais il est temps de présenter un code plus professionnel, incluant l'option de supprimer des pointeurs, que je viens de trouver dans ma bibliothèque personnelle:

# Émulation des pointeurs C :

from collections import deque # plus efficace que la liste pour ajouter des éléments
from sortedcontainers import SortedList # effectue add et discard en temps log(n)

class new:      
    # Émulation des pointeurs C :
    # à utiliser comme : p = new()
    #                   p.val             
    #                   p.val = quelque chose
    #                   p.address
    #                   get_address(p) 
    #                   del_pointer(p) 
    #                   null (un pointeur nul)

    __pointer_storage__ = SortedList(key = lambda p: p.address)
    __to_delete_pointers__ = deque()
    __pointer_address__ = 0 

    def __init__(self):      

        self.val = None 

        if new.__to_delete_pointers__:
            p = new.__to_delete_pointers__.pop()
            self.address = p.address
            new.__pointer_storage__.discard(p) # effectué en temps log(n) grâce à sortedcontainers
            new.__pointer_storage__.add(self)  # idem

        else:
            self.address = new.__pointer_address__
            new.__pointer_storage__.add(self)
            new.__pointer_address__ += 1

def get_pointer(address):
    return new.__pointer_storage__[address]

def get_address(p):
    return p.address

def del_pointer(p):
    new.__to_delete_pointers__.append(p)

null = new() # créer un pointeur nul, dont l'adresse est 0

0 votes

Je pense que vous avez simplement encadré les valeurs d'une manière étrange.

0 votes

Vous voulez dire : une "méthode intelligente" ou une "méthode pas intelligente" ?

0 votes

Uhhh... J'ai du mal à voir un cas d'utilisation valable pour un stockage global indexé par un nombre aléatoire.

-1voto

Abdlrahman Saber Points 126

Je ne sais pas si mon commentaire vous aidera ou non, mais si vous voulez utiliser pointeurs en python, vous pouvez utiliser des dictionnaires au lieu de variables

Disons que dans votre exemple sera

a = {'valeur': 1}

b = {'valeur': 2}

ensuite vous avez changé a en 2

a['valeur'] = 2

print(a) #{'valeur': 2}

0 votes

Cela n'a rien à voir avec un pointeur ou même essayer de connecter a et b. Changer la deuxième ligne en b = a a beaucoup plus de sens mais toujours, pas tellement lié à la question.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X