在python中模拟按值传递行为

时间:2009-05-10 11:05:50

标签: python pass-by-value

我想模仿python中的pass-by-value行为。换句话说,我想确保我写的函数不会修改用户提供的数据。

一种可能的方法是使用深层复制:

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

是否有更高效或更多 pythonic 方法来实现这一目标,尽可能少地假设传递的对象(例如.clone()方法)

修改

我知道技术上python中的所有内容都是按值传递的。我对模仿行为感兴趣,即确保我不会弄乱传递给函数的数据。我想最通用的方法是使用自己的克隆机制或深度复制来克隆有问题的对象。

8 个答案:

答案 0 :(得分:31)

没有pythonic方式这样做。

Python为强制执行等内容提供了极少的工具,例如私有或只读数据。 pythonic哲学是“我们都同意成年人”:在这种情况下,这意味着“函数不应该更改数据”是规范的一部分,但在代码中没有强制执行。


如果您想复制数据,最接近的是您的解决方案。但copy.deepcopy除了效率低下外,还有警告such as

  

因为深拷贝会复制它可能复制的所有内容,例如,即使在副本之间也应该共享的管理数据结构。

     

[...]

     

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

所以我只推荐它,如果你知道你正在处理内置的Python类型或你自己的对象(你可以通过定义__copy__ / __deepcopy__特殊来自定义复制行为方法,不需要定义自己的clone()方法。

答案 1 :(得分:30)

您可以制作装饰器并将克隆行为放入其中。

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

你甚至可以让装饰者采用某种功能进行克隆,从而允许装饰者的用户决定克隆策略。在这种情况下,装饰器可能最好实现为覆盖__call__的类。

答案 2 :(得分:8)

只有几个内置类型作为参考,例如list

因此,对于我来说,在本例中,列表的pythonic方式是:

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

list1[:]创建list1的新实例,您可以将其分配给新变量。

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

正如我之前所说,只有少数内置类型,它们的行为类似于引用,在此示例中为列表。

任何方式......希望它有所帮助。

答案 3 :(得分:3)

通常在将数据传递给外部API时,您可以通过将数据作为不可变对象传递来确保数据的完整性,例如将数据包装到元组中。如果这是您尝试通过代码阻止的内容,则无法修改此内容。

答案 4 :(得分:2)

我无法找出任何其他 pythonic 选项。但就我个人而言,我更喜欢 OO 方式。

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

def f(data):
  #do stuff

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

答案 5 :(得分:1)

虽然我确定没有真正的pythonic方法来做到这一点,但我希望pickle模块能够为您提供任何业务处理的所有内容的副本。

import pickle

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

答案 6 :(得分:0)

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]

答案 7 :(得分:0)

许多人使用标准库copy。我更喜欢在课堂上定义__copy____deepcopy__copy中的方法可能存在一些问题。

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

为避免这些失控行为,请通过覆盖__copy____deepcopy__定义自己的浅/深复制方法。 Alex's answer就是一个很好的例子。