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

StackOverflow https://stackoverflow.com/questions/845110

  •  20-08-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

Питонического способа сделать это не существует.

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


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

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

[...]

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

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

Итак, для меня питонический способ передачи по значению для списка в этом примере будет следующим:

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

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

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

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

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

обычно при передаче данных во внешний 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]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top