propias - paso por valor y paso por referencia python
Emulando comportamiento de paso por valor en python (7)
Me gustaría emular el comportamiento de paso por valor en python. En otras palabras, me gustaría estar absolutamente seguro de que la función que escribo no modifica 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
¿Existe una manera más eficiente o más pitón de lograr este objetivo, haciendo tan pocas suposiciones como sea posible sobre el objeto que se está pasando (como el método .clone ())?
Editar
Soy consciente de que técnicamente todo en Python se pasa por valor. Me interesaba emular el comportamiento, es decir, asegurarme de no alterar 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.
Además de la respuesta del usuario 695800 , pase por valor las 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]
Aunque estoy seguro de que no hay una manera realmente pitónica de hacer esto, espero que el módulo pickle
le brinde copias de todo lo que tiene algún negocio que considere un valor.
import pickle
def f(data):
data = pickle.loads(pickle.dumps((data)))
#do stuff
No hay una 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 somos adultos que consienten": en este caso, esto significa que "la función no debe 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 acercarse es su solución. Pero copy.deepcopy
, además de ser ineficiente, también tiene advertencias como :
Debido a que copia profunda copia todo lo que puede copiar demasiado, por ejemplo, estructuras de datos administrativos que deben 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 otro 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 es necesario definir su propio clone()
método).
No puedo imaginar ninguna otra opción pitónica . 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())
Por lo general, al pasar datos a una API externa, puede asegurar 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 intentaste evitar con tu código.
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 robusta y no maneja los argumentos de valor clave o los métodos de clase (falta de argumento propio), pero se obtiene 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 realice algún tipo de función que haga la clonación y, por lo tanto, permita que el usuario del decorador decida la estrategia de clonación. En ese caso, el decorador probablemente se implementa mejor como una clase con __call__
anulada.
Solo hay un par de tipos incorporados que funcionan como referencias, como la list
, por ejemplo.
Por lo tanto, para mí, la forma pitónica de hacer una relación paso a 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 anteriormente, solo hay unos pocos tipos incorporados, que su comportamiento es como referencias, listas en este ejemplo.
De cualquier manera ... espero que ayude.