Эмуляции передачи по значению поведение в Python


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

Один из возможных способов-использовать глубокое копирование:

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

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

Edit

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

7 51

7 ответов:

Нет никакого питонского способа сделать это.

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


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

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

[...]

Этот модуль не копирует такие типы, как модуль, метод, трассировка стека, фрейм стека, файл, сокет, окно, массив или любые подобные типы.

Поэтому я рекомендую его только в том случае, если вы знаете, что имеете дело со встроенными типами 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)

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

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

Так, для меня подходящие для Python способ для передачи по значению, по списку, в данном примере, составит:

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

Хотя я уверен, что на самом деле нет никакого питонского способа сделать это, я ожидаю, что модуль 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]