Domanda

Vorrei emulare il comportamento pass-by-value in Python.In altre parole, vorrei essere assolutamente sicuro che la funzione che scrivo non modifichi i dati forniti dall'utente.

Un modo possibile è utilizzare la copia profonda:

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

c'è più efficiente o più divinatorio modo per raggiungere questo obiettivo, facendo il minor numero possibile di ipotesi sull'oggetto passato (come il metodo .clone())

Modificare

Sono consapevole che tecnicamente tutto in Python viene passato per valore.mi interessava emulando il comportamento, cioèassicurandomi di non interferire con i dati passati alla funzione.Immagino che il modo più generale sia clonare l'oggetto in questione con il proprio meccanismo di clonazione o con deepcopy.

È stato utile?

Soluzione

Non esiste un modo Python per farlo.

Python fornisce pochissime funzionalità per applicare cose come dati privati ​​o di sola lettura.La filosofia pitonica è che "siamo tutti adulti consenzienti":in questo caso ciò significa che "la funzione non dovrebbe modificare i dati" fa parte delle specifiche ma non è imposto nel codice.


Se vuoi fare una copia dei dati, la soluzione più vicina che puoi ottenere è la tua.Ma copy.deepcopy, oltre ad essere inefficiente, presenta anche delle avvertenze ad esempio:

Poiché la copia profonda copia tutto ciò che potrebbe copiare troppo, ad esempio strutture di dati amministrativi che dovrebbero essere condivise anche tra copie.

[...]

Questo modulo non copia tipi come modulo, metodo, stack trace, stack frame, file, socket, finestra, array o tipi simili.

Quindi lo consiglierei solo se sai che hai a che fare con tipi Python integrati o con i tuoi oggetti (dove puoi personalizzare il comportamento di copia definendo il __copy__ / __deepcopy__ metodi speciali, non è necessario definirne uno tuo clone() metodo).

Altri suggerimenti

Puoi creare un decoratore e inserirvi il comportamento della clonazione.

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

Questo non è il modo più efficace e non gestisce argomenti di valori-chiave o metodi di classe (mancanza di argomenti autonomi), ma ottieni l'immagine.

Nota che le seguenti affermazioni sono uguali:

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

Potresti anche avere il decoratore ad assumere una sorta di funzione che fa la clonazione e quindi consentire all'utente del decoratore di decidere la strategia di clonazione. In tal caso, il decoratore è probabilmente meglio implementato come classe con __call__ sovrascritto.

Esistono solo un paio di caratteri predefiniti che funzionano come riferimenti, ad esempio list,

Quindi, per me il modo pitonico di fare un valore pass-by, per list, in questo esempio, sarebbe:

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

list1[:] crea una nuova istanza dell'elenco1 e puoi assegnarla a una nuova variabile.

Forse potresti scrivere una funzione che potrebbe ricevere un argomento, quindi controllarne il tipo e, di conseguenza, eseguire un'operazione incorporata che potrebbe restituire una nuova istanza dell'argomento passato.

Come ho detto prima, ci sono solo alcuni tipi predefiniti, che il loro comportamento è come riferimenti, elenchi in questo esempio.

Ad ogni modo ... spero che sia d'aiuto.

di solito quando si passano i dati a un'API esterna, è possibile assicurare l'integrità dei dati passandoli come oggetto immutabile, ad esempio avvolgere i dati in una tupla. Questo non può essere modificato, se è quello che hai cercato di impedire con il tuo codice.

Non riesco a capire altre opzioni pythonic . Ma personalmente preferirei il modo più OO .

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

def f(data):
  #do stuff

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

Anche se sono sicuro che non esiste un modo veramente pitonico per farlo, mi aspetto che il modulo pickle ti darà copie di tutto ciò che hai in affari trattando come un valore.

import pickle

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

Oltre alla risposta dell'utente695800, passa il valore per gli elenchi possibili con l'operatore [:]

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

chiamato con

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

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

list1
Out[14]: [1, 2, 3, 4]
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top