Pregunta

Me gustaría emular el comportamiento de paso por valor en python. En otras palabras, me gustaría asegurarme absolutamente de que la función que escribo no modifique los datos proporcionados por el usuario.

Una forma posible es usar una copia profunda:

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

¿hay alguna manera más eficiente o más pitónica de lograr este objetivo, haciendo la menor cantidad posible de suposiciones sobre el objeto que se pasa (como el método .clone ())

Editar

Soy consciente de que técnicamente todo en Python se pasa por valor. Estaba interesado en emular el comportamiento, es decir, asegurarme de no meterme con los datos que se pasaron a la función. Supongo que la forma más general es clonar el objeto en cuestión, ya sea con su propio mecanismo de clonación o con copia profunda.

¿Fue útil?

Solución

No hay forma pitónica de hacer esto.

Python proporciona muy pocas facilidades para hacer cumplir cosas tales como datos privados o de solo lectura. La filosofía pitónica es que & "; Todos estamos consintiendo adultos &"; En este caso esto significa que & "; La función no debería cambiar los datos &"; es parte de la especificación pero no se aplica en el código.


Si desea hacer una copia de los datos, lo más cerca que puede llegar es su solución. Pero copy.deepcopy, además de ser ineficiente, también tiene advertencias como :

  

Debido a que la copia profunda copia todo, puede copiar demasiado, por ejemplo, estructuras de datos administrativos que deberían compartirse incluso entre copias.

     

[...]

     

Este módulo no copia tipos como módulo, método, seguimiento de pila, marco de pila, archivo, socket, ventana, matriz o cualquier tipo similar.

Por lo tanto, solo lo recomendaría si sabe que está tratando con tipos de Python incorporados o sus propios objetos (donde puede personalizar el comportamiento de copia definiendo los métodos especiales __copy__ / __deepcopy__, no hay necesita definir su propio clone() método).

Otros consejos

Puedes hacer un decorador y poner el comportamiento de clonación en eso.

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

Esta no es la forma más sólida, y no maneja argumentos de valor clave o métodos de clase (falta de autoargumento), pero obtienes la imagen.

Tenga en cuenta que las siguientes declaraciones son iguales:

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

Incluso podría hacer que el decorador tome algún tipo de función que haga la clonación y, por lo tanto, permita al usuario del decorador decidir la estrategia de clonación. En ese caso, el decorador probablemente se implementa mejor como una clase con __call__ reemplazado.

Solo hay un par de tipos integrados que funcionan como referencias, como list, por ejemplo.

Entonces, para mí, la forma pitónica para hacer un paso por valor, para la lista, en este ejemplo, sería:

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

list1[:] crea una nueva instancia de la lista1, y puede asignarla a una nueva variable.

Tal vez podría escribir una función que podría recibir un argumento, luego verificar su tipo y, de acuerdo con ese resultado, realizar una operación integrada que podría devolver una nueva instancia del argumento pasado.

Como dije antes, solo hay unos pocos tipos integrados, que su comportamiento es como referencias, listas en este ejemplo.

De cualquier manera ... espero que ayude.

generalmente al pasar datos a una API externa, puede garantizar la integridad de sus datos pasándolos como un objeto inmutable, por ejemplo, envolver sus datos en una tupla. Esto no se puede modificar, si eso es lo que intentó evitar con su código.

No puedo encontrar ninguna otra opción pythonic . Pero personalmente prefiero la forma más OO .

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

def f(data):
  #do stuff

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

Aunque estoy seguro de que no hay una forma realmente pitónica de hacer esto, espero que el módulo pickle le proporcione copias de todo lo que tiene que tratar con valor.

import pickle

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

Además de la respuesta de user695800 , pase por valor para listas posibles con el operador [:]

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

llamado con

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

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

list1
Out[14]: [1, 2, 3, 4]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top