简体   繁体   English

Python将对象作为参数传递而不覆盖

[英]Python passing object as argument without overwriting

Consider this code: 考虑以下代码:

class MyClass:
    def __init__(self, x):
        self.x = x

def myfunct(arg):
    arg.x += 1
    return arg

a = MyClass(0)
print(a.x)

b = myfunct(a)
print(a.x)
print(b.x)

This returns: 返回:

0
1
1

I would expect this code to behave in the same way as this one: 我希望这段代码的行为与此代码相同:

def myfunct(arg):
    arg += 1
    return arg

c = 0
print(c)

d = myfunct(c)
print(c)
print(d)

However the latter returns: 但是,后者返回:

0
0
1

I understand this is due to Python's way of passing arguments by assignment, as explained in this post , or this post . 我了解这是由于Python通过赋值传递参数的方式(如本博文本博文中所述)

However, I can't figure out a way to work around the behavior exhibited in the first code, which is unwanted in the project I am working on. 但是,我找不到解决第一个代码中显示的行为的方法,这在我正在处理的项目中是不需要的。 How can I pass an object as an argument to a function, return a madified object, and keep the original one untouched? 如何将一个对象作为参数传递给函数,返回一个疯狂的对象,并保持原始对象不变?

You're explicitly modifying your object. 您正在显式修改对象。 Python supports this behavior by default, but if you want to prevent modification of your object you may want to update the __setattr__ to manage attribute modification. Python默认情况下支持此行为,但是如果要防止修改对象,则可能需要更新__setattr__来管理属性修改。

If you want to prevent the original object from modifying and you want to modify the object sent to the function you can add a __copy__ method to your object to be copyable in a way you like, then pass a copy of your object to the function using copy.copy() . 如果要防止原始对象被修改,并且想要修改发送给该函数的对象,则可以向对象添加__copy__方法以使其可以以自己喜欢的方式进行复制,然后使用将该对象的副本传递给该函数copy.copy()

class MyClass:
    def __init__(self, x):
        self.x = x

    # default copy  
    def __copy__(self):
        cls = self.__class__
        result = cls.__new__(cls)
        result.__dict__.update(self.__dict__)
        return result

Demo: 演示:

In [21]: a = MyClass(0)
    ...: print(a.x)
    ...: 
0
# import copy
In [22]: b = myfunct(copy.copy(a))

In [24]: a.x
Out[24]: 0

In [25]: b.x
Out[25]: 1

The simple solution is, just create or pass a copy. 简单的解决方案是仅创建或传递副本。 To do that you have to possibiltys. 为此,您必须要有可能。 Either you create a .copy() method on the class or use the copy module. 您可以在类上创建.copy()方法,也可以使用copy模块。

A copy method could look like this: 复制方法如下所示:

class MyClass:
    def __init__(self, x):
        self.x = x
    def copy(self):
        return self.__class__(self.x)

The copy module works like this: 复制模块的工作方式如下:

import copy
b = copy.copy(a)

You can use either way to create a function that simply returns a new copy of the argument: 您可以使用任何一种方式来创建仅返回参数新副本的函数:

def myfunct(arg):
    arg = arg.copy() # Or copy.copy(arg)
    arg.x += 1
    return arg

Edit: As many other answers say, my approach shown above doesn't work if you have mutable objects in mutable objects (as example an object of your class, that has another object of your class in its args attribute). 编辑:正如许多其他答案所说,如果您在可变对象中有可变对象(例如您的类的对象,其args属性中具有该类的另一个对象),则上面显示的方法无效。 In that case use the copy.deepcopy function instead: 在这种情况下,请使用copy.deepcopy函数:

def myfunct(arg):
    arg =  copy.deepcopy(arg)
    arg.x += 1
    return arg
from copy import deepcopy
def myfunct(arg):
    new_arg = deepcopy(arg)
    new_arg.x += 1
    return new_arg

I would recommend deepcopy over copy since you want to make sure that all references to the original object are cut. 我建议深度复制而不是复制,因为您要确保切下所有对原始对象的引用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM