【问题标题】:Relink python object (i.e. pass by reference)重新链接 python 对象(即通过引用传递)
【发布时间】:2011-06-23 02:50:58
【问题描述】:

我想使用一个函数来操作输入参数,但是在此过程中我必须复制输入参数。我不想返回修改后的对象(因为可能涉及也可能不涉及多处理)。如何将输入参数重新分配给复制的值?这是函数的表示:

from copy import deepcopy, copy

def myFunc(myObj,i):
    myObj.x = 'start'
    # I can't avoid this step:
    modObj = deepcopy(myObj)
    modObj.x = 'modified'
    if i == 0:
        myObj.x = modObj.x
    elif i == 1:
        myObj = modObj
    elif i == 2:
        myObj = copy(modObj)
    elif i == 3:
        myObj.__dict__ = modObj.__dict__

class MyClass(object):
   def __init__(self):
       self.x = "construct"

for i in range(4):
   temp = MyClass()
   myFunc(temp,i)
   print i,temp.x

这是输出:

0 modified
1 start
2 start
3 modified

既然选项 #3 与我想要做的非常接近,有没有“官方”的方式来做呢?例如

myObj.shallowcopy(modObj)

这个浅拷贝的行为类似于拷贝中的浅拷贝。一种替代方法是将 MyClass 作为某个其他类的成员嵌入,并将该类传递给函数......但是如果我能做到这一点,为什么 python 不能通过复制成员数据来节省我的那一步?我读过this,但他们似乎没有为我找到答案。

【问题讨论】:

  • 该问题的第一个答案也应该回答您的问题。
  • 为什么需要复制对象?多处理的可能性如何阻止您返回修改后的副本?
  • @Devin,对于添加的multiprocessing 皱纹,我认为这是一个重复的 except。不过,我建议 amos 编辑标题以将其合并。
  • 像 Karl 一样,我想知道为什么您需要对象的副本。我不知道这是不是他的想法,但我的问题是:你为什么不能只修改 original 对象?这四个选项是您的功能的所有基本功能,还是它们是“我尝试通过四种方式实现我的功能正在尝试执行的一个功能”意义上的选项?

标签: python reference pass-by-reference


【解决方案1】:

更新:好的,既然我对这个问题有了更多了解,我建议将选项 1 和 4 结合起来:创建公共 update()dictify() 方法。这些 可能 是选项 4 的包装或类似myObj.__dict__.update(modObj.__dict__) 的一些变体——这两者都有些骇人听闻。

或者,它们可以是优雅、可读、自记录的方法,它们接受和返回(分别)self 的适当字典表示,这可能与 self.__dict__ 一致,也可能不一致。我认为 Python 没有任何内置功能的原因是没有真正通用的方法来实现它,因为并非所有对象都有__dict__。这种行为必须有一个定制的实现。

如果操作 __dict__ 足以满足您的需求,那很好,但是如果您将操作隐藏在可以稍后更改的抽象后面,如果您决定要实现 __slots__ 或类似的东西,您可以节省一些麻烦.

(出于历史目的保留以下内容)


这让我很困惑:“我不想返回修改后的对象(因为可能涉及也可能不涉及多处理)。”如果您的意思是 multiprocessing 模块,那么即使选项 0 和 3 也会失败,因为进程不共享内存。

>>> for i in range(4):
...     temp = MyClass()
...     p = Process(target=myFunc, args=(temp, i))
...     p.start()
...     p.join()
...     print i,temp.x
... 
0 construct
1 construct
2 construct
3 construct

看到了吗?如果要在进程之间传输数据,则必须使用multiprocessing 的内置类型之一。

>>> from multiprocessing import Process, Queue
>>> 
>>> def myFuncQ(myObj, i, return_queue):
...     myObj.x = 'start'
...     modObj = deepcopy(myObj)
...     modObj.x = 'modified'
...     return_queue.put(modObj)
... 
>>> return_queue = Queue()
>>> for i in range(4):
...     temp = MyClass()
...     p = Process(target=myFuncQ, args=(temp, i, return_queue))
...     p.start()
...     p.join()
...     temp = return_queue.get()
...     print i,temp.x
... 
0 modified
1 modified
2 modified
3 modified

如果您不使用multiprocessing,那么Queue 将与任何其他可变容器一样用于此目的。这是最接近 Python 中通过引用传递的“官方”方式。

【讨论】:

  • 对不起,我应该澄清这一点。我将此函数设置为兼容或不兼容多处理,因为我想支持 python 2.5(出于不再正确的原因)。当我使用多处理时,我将我的对象打印到文件(此数据结构的标准格式是 .pdb 文件),当我不使用多处理时,对象共享内存。我认为这里的关键问题是,在我的代码中,myObj 不能被腌制,因为为所有底层 c++ 对象启用腌制会过于繁琐。我相信这排除了多处理。
  • @amos, Ah -- “底层 c++ 对象” == 缺少的关键信息。那么重点仍然存在:您只能通过容器在 python 中“通过引用传递”。事实上,myObj.__dict__ = modObj.__dict__ 起作用的原因是您将myObj 视为一个容器——并且以一种破坏抽象的方式,因为并非所有对象都有__dict__。所以它做你想让它做的事情更多的是巧合,而不是表明myObj.__dict__ = modObj.__dict__是一件好事。
  • 你说“这种行为必须有一个自定义的实现。”......但我不明白......考虑a=b,这使得一个新的a分享b 的指针。我想要的是让原来的a 共享b 的指针。它应该像重新分配单个指针一样简单,对吧?
  • 无论如何,我选择了类似于您的“历史”答案的内容。除了输入之外,我的函数签名还有一个输出容器。
  • 就像在相关范围内重新分配单个指针一样简单。假设你有def func(val): val = newvala = 5,你打电话给func(a);然后在func 内部,a 不存在,句号。只有val 存在,一旦func 退出,val 就会消失。所以a 不能被func 改变。 a 指向的东西 可以更改,a 可以更改在其范围内(使用def func(val): return newval,然后是a = func(a))。但是要更改 a 本身,您需要引用 a,而不仅仅是它指向的东西。
猜你喜欢
  • 1970-01-01
  • 2019-05-05
  • 2023-03-30
  • 1970-01-01
  • 2017-09-11
  • 2021-08-15
  • 2016-05-27
  • 2021-12-10
相关资源
最近更新 更多