在 Python 中模拟传值行为

问题描述 投票:0回答:8

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

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

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

有没有更高效或更Pythonic的方法来实现这个目标,对传递的对象做出尽可能少的假设(例如

.clone()
方法)?

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

    

python pass-by-value
8个回答
50
投票

Python 提供的工具很少用于

强制执行

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

如果您想复制数据,您可以获得的最接近的解决方案就是您的解决方案。但是
copy.deepcopy

,除了效率低之外,还有一些注意事项

比如

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

[...]

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

所以我只推荐它,如果你知道你正在处理内置的Python类型或你自己的对象(你可以通过定义
__copy__

/

__deepcopy__
特殊方法来自定义复制行为,无需定义您自己的
clone()
方法)。
    


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__

的类。

    


18
投票
list


所以,对于我来说,在这个例子中,对列表进行值传递的Python方式是:

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

list1[:]

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


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

正如我之前所说,只有少数内置类型,它们的行为就像本例中的引用、列表。

无论如何...希望有帮助。


4
投票
pythonic

选项。 但就我个人而言,我更喜欢更OO的方式。 class TheData(object): def clone(self): """return the cloned""" def f(data): #do stuff def caller(): d = TheData() f(d.clone())



4
投票


2
投票
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]



1
投票
pickle

模块将为您提供任何业务视为价值的所有内容的副本。


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



0
投票
copy

。我更喜欢在课堂上定义 __copy__

__deepcopy__
copy
中的方法可能存在一些问题。

浅拷贝将保留对原始对象的引用,而不是创建新的对象。
  1. 深度复制会递归运行,有时可能会导致死循环。如果没有足够的注意力,记忆力可能会爆炸。
  2. 为了避免这些失控行为,请通过覆盖
__copy__

__deepcopy__
来定义自己的浅/深复制方法。
Alex 的回答
给出了一个很好的例子。

© www.soinside.com 2019 - 2024. All rights reserved.