在Python中模拟值传递行为

78 投票
8 回答
94505 浏览
提问于 2025-04-15 11:29

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

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

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

有没有更有效或者更“Pythonic”的方法来实现这个目标,尽量少假设传入对象的特性(比如说不依赖于.clone()方法)呢?

编辑

我知道从技术上讲,Python中的一切都是按值传递的。我想要的是“模拟”这种行为,也就是确保我不会弄乱传给函数的数据。我想最通用的方法是使用对象自己的克隆机制或者深拷贝来克隆相关对象。

8 个回答

18

在Python中,只有几个内置类型可以作为引用使用,比如list(列表)。

所以,对我来说,如果想要在这个例子中实现列表的值传递,最简单的方法就是:

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

list1[:] 这个操作会创建一个新的列表实例,你可以把它赋值给一个新的变量。

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

正如我之前提到的,只有少数几个内置类型的行为像引用,这里以列表为例。

总之,希望这些信息对你有帮助。

41

你可以创建一个装饰器,把克隆的功能放在里面。

>>> 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__方法。

49

这件事没有特别“python风格”的做法。

Python 提供的功能很少,无法强制执行一些规则,比如私有数据或只读数据。Python 的理念是“我们都是成年人”:在这种情况下,这意味着“函数不应该改变数据”是一个规范,但在代码中并没有强制执行。


如果你想复制数据,最接近的办法就是你自己的解决方案。但是 copy.deepcopy 除了效率低下外,还有一些注意事项 比如

因为深拷贝会复制所有内容,所以可能会复制过多,比如一些应该在多个副本之间共享的管理数据结构。

[...]

这个模块不会复制像模块、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组或任何类似类型的东西。

所以我只建议在你确定处理的是内置的 Python 类型或你自己定义的对象时使用(在这种情况下,你可以通过定义 __copy__ / __deepcopy__ 特殊方法来定制复制行为,不需要自己定义 clone() 方法)。

撰写回答