web-dev-qa-db-fra.com

Passer un entier par référence en Python

Comment puis-je passer un entier par référence en Python?

Je souhaite modifier la valeur d'une variable que je suis en train de transmettre à la fonction. J'ai lu que tout en Python est transmis valeur par valeur, mais il doit y avoir une astuce facile. Par exemple, en Java, vous pouvez passer les types de référence Integer, Long, etc.

  1. Comment puis-je passer un entier dans une fonction par référence?
  2. Quelles sont les meilleures pratiques?
57
CodeKingPlusPlus

Cela ne fonctionne pas vraiment de cette façon en Python. Python passe des références aux objets. Dans votre fonction, vous avez un objet - Vous êtes libre de le muter (si possible). Cependant, les entiers sont immuables . Une solution de contournement consiste à transmettre le nombre entier dans un conteneur pouvant être muté:

def change(x):
    x[0] = 3

x = [1]
change(x)
print x

C'est au mieux laid/maladroit, mais vous ne ferez pas mieux en Python. La raison en est qu’en Python, l’affectation (=) prend un objet quelconque qui est le résultat du côté droit et le lie à ce qui se trouve du côté gauche * (ou le passe à la fonction appropriée).

En comprenant cela, nous pouvons voir pourquoi il n’existe aucun moyen de modifier la valeur d’un objet immuable dans une fonction - vous ne pouvez modifier aucun de ses attributs car il est immuable, et vous ne pouvez pas simplement attribuer à la "variable" une nouvelle valeur car vous créez en fait un nouvel objet (qui est différent de l'ancien) et lui donnez le nom que l'ancien avait dans l'espace de noms local.

Généralement, la solution de contournement consiste à simplement retourner l'objet que vous voulez:

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

* Dans le premier exemple ci-dessus, 3 est en fait passé à x.__setitem__.

74
mgilson

Dans la plupart des cas où vous auriez besoin de passer par référence, vous devez renvoyer plusieurs valeurs à l'appelant. Une "meilleure pratique" consiste à utiliser plusieurs valeurs de retour, ce qui est beaucoup plus facile à faire en Python que dans des langages tels que Java.

Voici un exemple simple:

def RectToPolar(x, y):
    r = (x ** 2 + y ** 2) ** 0.5
    theta = math.atan2(y, x)
    return r, theta # return 2 things at once

r, theta = RectToPolar(3, 4) # assign 2 things at once
21
Gabe

En réalité, la meilleure pratique consiste à prendre du recul et à vous demander si vous avez vraiment besoin de le faire. Pourquoi voulez-vous modifier la valeur d'une variable que vous transmettez à la fonction?

Si vous devez le faire rapidement, le moyen le plus rapide est de passer un list contenant le nombre entier et de coller un [0] autour de chaque utilisation, comme le montre la réponse de mgilson.

Si vous avez besoin de le faire pour quelque chose de plus important, écrivez une class ayant un int comme attribut, afin que vous puissiez simplement le définir. Bien sûr, cela vous oblige à donner un bon nom à la classe et à l'attribut. Si vous ne pouvez penser à rien, relisez la phrase plusieurs fois, puis utilisez la variable list.

Plus généralement, si vous essayez de porter directement un idiome Java sur Python, vous le faites mal. Même lorsqu'il y a quelque chose qui correspond directement (comme avec static/@staticmethod), vous ne voulez toujours pas l'utiliser dans la plupart des programmes Python simplement parce que vous l'utiliseriez en Java.

5
abarnert
class PassByReference:
    def Change(self, var):
        self.a = var
        print(self.a)
s=PassByReference()
s.Change(5)     
2
shruti

En Python, chaque valeur est une référence (un pointeur sur un objet), tout comme les non-primitives en Java. En outre, à l'instar de Java, Python ne dispose que d'une valeur de transmission par valeur. Donc, sémantiquement, ils sont à peu près les mêmes.

Puisque vous avez mentionné Java dans votre question, j'aimerais voir comment vous réalisez ce que vous voulez en Java. Si vous pouvez le montrer en Java, je peux vous montrer comment le faire exactement en Python.

1
newacct

Un tableau numpy à élément unique est mutable et pourtant, dans la plupart des cas, il peut être évalué comme s'il s'agissait d'une variable numérique python. Par conséquent, il s'agit d'un conteneur par numéro de référence plus pratique qu'une liste à un seul élément.

    import numpy as np
    def triple_var_by_ref(x):
        x[0]=x[0]*3
    a=np.array([2])
    triple_var_by_ref(a)
    print(a+1)

sortie:

3
1
Tal

Peut-être que ce n'est pas la façon Pythonic, mais vous pouvez le faire

import ctypes

def incr(a):
    a += 1

x = ctypes.c_int(1) # create c-var
incr(ctypes.ctypes.byref(x)) # passing by ref
1
Sergey Kovalev

Pas exactement en transmettant directement une valeur, mais en l’utilisant comme si elle avait été transmise.

x = 7
def my_method():
    nonlocal x
    x += 1
my_method()
print(x) # 8

Mises en garde:

  • nonlocal a été introduit en python 3
  • Si la portée englobante est la portée globale, utilisez global au lieu de nonlocal.
0
Uri

Peut-être un peu plus auto-documentant que le tour de la liste-de-longueur-1 est l'ancien tour de type vide:

def inc_i(v):
    v.i += 1

x = type('', (), {})()
x.i = 7
inc_i(x)
print(x.i)
0
mheyman

En Python, tout est passé par valeur, mais si vous souhaitez modifier un état, vous pouvez modifier la valeur d'un entier dans une liste ou un objet transmis à une méthode.

0
recursive