Эмуляция поведения pass-by-value в python

Я хотел бы эмулировать поведение pass-by-value в python. Другими словами, Я хотел бы абсолютно убедиться, что функция, которую я пишу, не изменяет предоставленные пользователем данные.

один из возможных способов - использовать deep copy:

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

есть ли более эффективные или более весть способ достижения этой цели, делая как можно меньше предположений о передаваемом объекте (например .метод clone ())

редактировать

Я знаю, что технически все в python передается по значению. Мне было интересно эмуляция поведение, т. е. убедитесь, что я не связываюсь с данными, которые были переданы функции. Я думаю, самый общий способ-клонировать объект либо с собственным механизмом клонирования или с deepcopy.

7 ответов


нет для Python способ сделать это.

Python предоставляет очень мало возможностей для исполнение такие вещи, как личные или только для чтения данных. Философия питона заключается в том, что "мы все взрослые люди": в этом случае это означает, что "функция не должна изменять данные" является частью спецификации, но не применяется в коде.


если вы хотите сделать копию данных, ближайший вы можете получить это ваше решение. Но!--0-->, кроме того, что неэффективно, также имеет предостережения например:

потому что deep copy копирует все, что может копировать слишком много, например, административные структуры данных, которые должны совместно использоваться даже между копиями.

[...]

этот модуль не копирует такие типы, как module, method, stack trace, Stack frame, file, socket, window, array или любые подобные типы.

поэтому я бы рекомендовал его, только если вы знаете, что имеете дело с встроенные типы Python или ваши собственные объекты (где вы можете настроить поведение копирования, определив __copy__ / __deepcopy__ специальные методы, нет необходимости определять свой собственный clone() метод).


вы можете сделать декоратора и поместить поведение клонирования в это.

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

Это не самый надежный способ и не обрабатывает аргументы ключевого значения или методы класса (отсутствие аргумента self), но вы получаете картину.

обратите внимание, что следующие операторы равны:

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

вы даже можете заставить декоратора взять какую-то функцию, которая делает клонирование и, таким образом, позволяет пользователю декоратора решить стратегию клонирования. В том case декоратор, вероятно, лучше всего реализован как класс с __call__ переопределяется.


есть только несколько встроенных типов, которые работают как ссылки, например list, например.

Итак, для меня питоническим способом для выполнения pass-by-value, для list, в этом примере, было бы:

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

list1[:] создает новый экземпляр list1, и вы можете назначить его новой переменной.

возможно, вы могли бы написать функцию, которая могла бы получить один аргумент, затем проверить ее тип и, согласно этому результату, выполнить встроенную операцию, которая могла бы возвращает новый экземпляр переданного аргумента.

как я уже говорил ранее, есть только несколько встроенных типов, что их поведение похоже на ссылки, списки в этом примере.

В любом случае... надеюсь, это поможет.


обычно при передаче данных во внешний API вы можете гарантировать целостность своих данных, передавая их как неизменяемый объект, например, оберните свои данные в кортеж. Это нельзя изменить, если это то, что вы пытались предотвратить своим кодом.


Я не могу понять другого весть. Но лично я предпочел бы больше ОО путь.

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

def f(data):
  #do stuff

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

хотя я уверен, что есть не очень подходящие для Python способ сделать это, я ожидаю, что pickle модуль даст вам копии всего, что у вас есть лечение как ценность.

import pickle

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

дальше user695800 это ответ, проходите мимо за списки с [: оператор]

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

позвонил с

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

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

list1
Out[14]: [1, 2, 3, 4]