17

What is the best practice to implement the copy method in a class?

Here is an example of my class:

class MyClass:
 def __init__(self, foo, bar=None):
 self.foo = foo
 if bar is not None:
 self.bar = bar
 else:
 self.bar = {'Hello': 'Ciao'}

I found a post of five years ago that suggests the following way:

import copy
def copy(self):
 return MyClass(copy.copy(self.foo), copy.copy(self.bar))

Is it still the only way to do it or are there other possibilities?

I need to create a copy of my object to avoid that a function changes the original object. The function is something like this:

def translate_and_print(my_class, dict={'Hello':'Ciao'}):
 temp = my_class # here I want to use the copy method: temp = my_class.copy()
 temp.foo = temp.bar[temp.foo]
 print(temp.foo)

The output of the following code is "Ciao", "Ciao" but should be "Ciao", "Hello"

mc = MyClass('Hello')
translate_and_print(mc)
print(mc.foo)

If I use the copy() method I have the error:

AttributeError: 'MyClass' object has no attribute 'copy'

smci
34.2k21 gold badges118 silver badges152 bronze badges
asked Jul 12, 2017 at 7:57
3
  • 1
    With regard to your updated comment, I can run myobject = MyClass('foo'), and then do copy.copy(myobject) without any issues. Can you provide a piece of code that reproduces your error? Commented Jul 12, 2017 at 10:40
  • 1
    Leaving the above comment in place for historical reasons. It seems your error is trying to call my_class.copy(), whereas you should be calling copy.copy(my_class). Commented Jul 12, 2017 at 10:42
  • 1
    My post already includes it, specifically "you can simply call copy.copy(myobject)" in the end. Commented Jul 12, 2017 at 10:53

3 Answers 3

15

You should implement the __copy__ method, and possibly also __deepcopy__. The documentation states:

In order for a class to define its own copy implementation, it can define special methods __copy__() and __deepcopy__(). The former is called to implement the shallow copy operation; no additional arguments are passed. The latter is called to implement the deep copy operation; it is passed one argument, the memo dictionary. If the __deepcopy__() implementation needs to make a deep copy of a component, it should call the deepcopy() function with the component as first argument and the memo dictionary as second argument.

With that said, unless you do some magic (e.g. C allocations, or calls to statefull C libraries), you should not need to implement __copy__ yourself, but python provides this for you for free, and you can simply call copy.copy(myobject)

answered Jul 12, 2017 at 7:59
Sign up to request clarification or add additional context in comments.

4 Comments

Good thank you for the suggestion but this doesn't explain how to implement the method.
In your example, there is no reason to implement __copy__. If you have a use case where you actually use some low-level resources that require you to implement it, could you update your example and we can help out with that? In your example, the implementation you suggest adds nothing since python does that by default.
I edited the question. The copy() method is not implemented in my class.
Great advice at end. I was trying create copy method when unnecessary. copy.copy was magic bullet
10

You got an AttributeError, I think that's because you just create a copy method, instead of that you need to create __copy__ method.

import copy
class MyClass:
 def __init__(self, name):
 self.name = name
 def __copy__(self):
 return MyClass(self.name)
 def __deepcopy__(self, memo):
 return MyClass(copy.deepcopy(self.name, memo))

__copy__ method for shallow copy and __deepcopy__ for deep copy.

I hope you got the point, Here is the link i follow

answered Jul 14, 2017 at 3:57

2 Comments

This might cause bugs in child classes due to implicit usage of MyClass initialiser. For example, copy of child class instance would become an instance of MyClass.
But that has the inconvenient of requiring import copy! On the other hand just def copy(self) instead of def __copy__(self) has no such requirement.
5

Follow implementation makes just copy, without calling the init() method.

class MyClass(BaseClass):
 def __init__(self, name):
 super().__init__()
 self.name = name
 def __copy__(self):
 obj = type(self).__new__(self.__class__)
 obj.__dict__.update(self.__dict__)
 return obj
answered Jun 26, 2018 at 13:01

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.