2

In "How to override the copy/deepcopy operations for a Python object?" post, it is written in one of the answers that:

    def __deepcopy__(self, memo):
    cls = self.__class__
    result = cls.__new__(cls)
    memo[id(self)] = result
    for k, v in self.__dict__.items():
        setattr(result, k, deepcopy(v, memo))
    return result

Is a possible way of overriding the deepcopy method. I do not understand what each of the lines does. What is the purpose of this implementation? Can't I just create my own copy method with something specific for my class like the following?

def my_copy(self, original_obj):
    obj = MyClass()
    obj.a = (copy of a from the other object)
    obj.b = (copy of b from the other object)
    ...
    return obj
Miguel
  • 1,295
  • 12
  • 25
  • 1
    Purpose: Sometimes your class contains attributes that can either not be deep-copied or that you do not want to copy but instead share the existing object (e.g. weak references, DB connections, ...). In the ``for`` loop in the example, you could filter such attributes and handle them as you need. – Mike Scotty Jul 24 '19 at 11:33

1 Answers1

3

You are right you could to this in the way you presented. However this would be specific to your object implementation. The example you posted is much more generic and could handle copying many different classes of objects. It could also be made as a mixin to easy add it to your class.

The code presented, do this by:

def __deepcopy__(self, memo):
        cls = self.__class__ # Extract the class of the object
        result = cls.__new__(cls) # Create a new instance of the object based on extracted class
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v, memo)) # Copy over attributes by copying directly or in case of complex objects like lists for exaample calling the `__deepcopy()__` method defined by them. Thus recursively copying the whole tree of objects.
        return result

Note also that if your class consisted of complex attributes like lists you would also need to call directly deepcopy on them or otherwise you would end up with a shallow copy for some of the attributes.

EDIT

memo is a dict, where id-to-object correspondence is kept to reconstruct complex object graphs perfectly.

CodeSamurai777
  • 3,285
  • 2
  • 24
  • 42
  • also note that if your class isn't using `__dict__` to store its attributes (e.g. it's using `__slots__`) then this implementation won't do the right thing – Sam Mason Jul 24 '19 at 11:39
  • Thanks, I've 2 questions. First, why the first 2 lines instead of doing result = ClassName(...)? Aren't both creating a new object? Also, what is the "memo" and what is that line needed for? – Miguel Jul 24 '19 at 11:42
  • @SamMason where is it defined what the class is using to store its attributes? – Miguel Jul 24 '19 at 11:43
  • @SamMason that is correct that is why I didn't write it could handle all classes but many :) – CodeSamurai777 Jul 24 '19 at 11:44
  • @Miguel Because you do not know the class name the `__class__` attribute store the class object not its name if you would like to extract name you should retrieve it from that object as such `__class__.__name__` but that would give you a string which is not helpful at all. So you get the class object and then call its `__new__` method which gives you a blank new object of the correct class. – CodeSamurai777 Jul 24 '19 at 11:49
  • @mrangry777 Oh, I thought the deepcopy was being overwritten as a method of the class you want to overwrite it for. So you would always know the name of the class. But as I understand it now, you can only have 1 definition of deepcopy for all your classes. – Miguel Jul 24 '19 at 11:53
  • 1
    @Miguel classes use `__dict__` by default. if you wrote the class yourself you'll know what it's using (i.e. probably the default) and if you didn't then try searching for `__slots__` in the definition. maybe also check out https://stackoverflow.com/q/472000/1358308 – Sam Mason Jul 24 '19 at 11:54
  • 1
    @Miguel Yes it is overwritten for that one class. However if you create a base class with a generic deepcopy like example you posted you will not have to redefine it in all child classes. If you use the class name explicitly inheritance will break your code. – CodeSamurai777 Jul 24 '19 at 11:56