模拟 Python 中的逐值传递行为

我想模拟 python 中的按值传递行为。换句话说,我要确保我编写的函数不会修改用户提供的数据。

一种可能的方法是使用深度拷贝:

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

是否有更有效或更多的 蟒蛇方法来实现这个目标,尽可能少地假设要传递的对象(例如。克隆()方法)

编辑

我知道,从技术上讲,Python 中的所有内容都是通过值传递的。我对 模仿的行为很感兴趣,也就是说,确保我不会弄乱传递给函数的数据。我想最通用的方法是用它自己的克隆机制或者深拷贝来克隆所讨论的对象。

83692 次浏览

我想不出任何其他的 蟒蛇选项。但就我个人而言,我更喜欢 的方式。

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


def f(data):
#do stuff


def caller():
d = TheData()
f(d.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

这不是最健壮的方法,也不能处理键值参数或类方法(缺乏自身参数) ,但是您可以看到图片。

请注意,以下语句是相等的:

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

您甚至可以让 decator 使用某种函数进行克隆,从而允许 decator 的用户决定克隆策略。在这种情况下,装饰器可能最好实现为一个重写了 __call__的类。

没有蟒蛇式的方法可以做到这一点。

Python 很少为 强制执行提供诸如私有或只读数据之类的工具。Pythonic 的哲学是“我们都是成人协议”,在这种情况下,这意味着“函数不应该改变数据”是规范的一部分,但不是在代码中强制执行的。


如果您想要复制数据,那么最接近的方法就是您的解决方案。但 copy.deepcopy除了效率低下之外,还有一点值得注意:

因为深度拷贝会拷贝太多的东西,例如,即使在拷贝之间也应该共享的管理数据结构。

[...]

此模块不复制模块、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组或任何类似类型。

因此,我只建议您在处理内置 Python 类型或自己的对象时使用它(在这里,您可以通过定义 __copy__/__deepcopy__特殊方法来定制复制行为,不需要定义自己的 clone()方法)。

通常,当把数据传递给外部 API 时,可以通过将数据作为不可变物件传递来确保数据的完整性,例如将数据包装成 tuple。如果您的代码试图阻止这种情况发生,则不能对其进行修改。

虽然我确信没有真正的 Python 方式来做到这一点,我希望 pickle模块将给你的任何业务作为一个价值处理的一切副本。

import pickle


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

只有几种内置类型可以用作引用,例如 list

因此,对我来说,在这个例子中,执行通过值传递的 Python 方法是:

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

list1[:]创建 list1的一个新实例,您可以将它赋给一个新变量。

也许您可以编写一个可以接收一个参数的函数,然后检查它的类型,并根据该结果执行一个内置操作,该操作可以返回传递的参数的新实例。

正如我前面所说,在这个例子中,只有少数内置类型的行为类似于引用列表。

不管怎样,希望能有帮助。

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]

许多人使用标准库 收到。我更喜欢在我的类中定义 __copy__或者 __deepcopy__copy中的方法可能存在一些问题。

  1. 浅表副本将保留对原始对象的引用,而不是创建新的对象。
  2. 深拷贝会递归运行,有时会导致死循环。如果没有足够的注意力,内存可能会爆炸。

为了避免这些失控的行为,可以通过覆盖 __copy____deepcopy__来定义自己的浅/深拷贝方法。Alex 的回答给出了一个很好的例子。