Question

Je voudrais émuler le comportement passage par valeur en python. En d'autres termes, je voudrais absolument m'assurer que la fonction que j'écris ne modifie pas les données fournies par l'utilisateur.

Une solution possible consiste à utiliser la copie en profondeur:

from copy import deepcopy
def f(data):
    data = deepcopy(data)
    #do stuff

Existe-t-il un moyen plus efficace ou plus pythonique d'atteindre cet objectif, en formulant le moins possible d'hypothèses sur l'objet passé (comme la méthode .clone ())

Modifier

Je suis conscient du fait que techniquement tout en python est passé par valeur. Je souhaitais émuler le comportement, c’est-à-dire s’assurer que je ne me mêlais pas des données transmises à la fonction. Je suppose que la méthode la plus générale consiste à cloner l'objet en question avec son propre mécanisme de clonage ou avec une copie en profondeur.

Était-ce utile?

La solution

Il n’existe aucun moyen pythonique de le faire.

Python fournit très peu de fonctionnalités pour appliquer des éléments tels que des données privées ou en lecture seule. La philosophie pythonique est que & «Nous sommes tous des adultes consentants»! Dans ce cas, cela signifie que & «La fonction ne doit pas changer les données &»; fait partie de la spécification mais n'est pas appliquée dans le code.

Si vous souhaitez effectuer une copie des données, vous pouvez vous rapprocher de votre solution. Mais copy.deepcopy, en plus d’être inefficace, présente également des réserves telles que :

  

Étant donné que la copie profonde copie tout, elle peut en copier trop, par exemple des structures de données administratives devant être partagées même entre des copies.

     

[...]

     

Ce module ne copie pas les types tels que module, méthode, trace de pile, cadre de pile, fichier, socket, fenêtre, tableau ou autres types similaires.

Je ne le recommanderais donc que si vous savez que vous utilisez des types Python intégrés ou vos propres objets (où vous pouvez personnaliser le comportement de copie en définissant les __copy__ / __deepcopy__ méthodes spéciales, sans besoin de définir votre propre clone() méthode).

Autres conseils

Vous pouvez créer un décorateur et y mettre le comportement de clonage.

>>> def passbyval(func):
def new(*args):
    cargs = [deepcopy(arg) for arg in args]
    return func(*cargs)
return new

>>> @passbyval
def myfunc(a):
    print a

>>> myfunc(20)
20

Ce n'est pas la méthode la plus robuste, et ne gère pas les arguments clé-valeur ni les méthodes de classe (absence de self-argument), mais vous obtenez l'image.

Notez que les instructions suivantes sont égales:

@somedecorator
def func1(): pass
# ... same as ...
def func2(): pass
func2 = somedecorator(func2)

Vous pouvez même demander au décorateur de prendre une sorte de fonction qui effectue le clonage, permettant ainsi à son utilisateur de décider de la stratégie de clonage. Dans ce cas, le décorateur est probablement mieux implémenté en tant que classe avec __call__ remplacement.

Seuls quelques types intégrés fonctionnent comme références, comme list par exemple.

Donc, pour moi, le moyen pythonique de créer une valeur de passage pour la liste, dans cet exemple, serait:

list1 = [0,1,2,3,4]
list2 = list1[:]

list1[:] crée une nouvelle instance de la liste1 et vous pouvez l'affecter à une nouvelle variable.

Vous pourriez peut-être écrire une fonction pouvant recevoir un argument, puis vérifier son type et, en conséquence, effectuer une opération intégrée pouvant renvoyer une nouvelle instance de l'argument transmis.

Comme je l’ai dit plus tôt, il n’existe que quelques types intégrés dont le comportement ressemble à celui des références, listes dans cet exemple.

De toute façon ... espérons que cela aide.

Habituellement, lors de la transmission de données à une API externe, vous pouvez assurer l'intégrité de vos données en les transmettant sous la forme d'un objet immuable, par exemple en enveloppant vos données dans un tuple. Cela ne peut pas être modifié, si c'est ce que vous avez essayé d'empêcher avec votre code.

Je ne peux trouver aucune autre option pythonique . Personnellement, je préférerais davantage OO .

class TheData(object):
  def clone(self):  """return the cloned"""

def f(data):
  #do stuff

def caller():
  d = TheData()
  f(d.clone())

Bien que je sois sûr qu’il n’existe aucun moyen vraiment pythonique de le faire, j’espère que le module pickle vous donnera des copies de tout ce que vous considérez comme une valeur.

import pickle

def f(data):
    data = pickle.loads(pickle.dumps((data)))
    #do stuff

En complément de la réponse de l'utilisateur 695800 , transmettez valeur pour les listes possible avec l'opérateur [:]

def listCopy(l):
    l[1] = 5
    for i in l:
        print i

appelé avec

In [12]: list1 = [1,2,3,4]

In [13]: listCopy(list1[:])
1
5
3
4

list1
Out[14]: [1, 2, 3, 4]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top