How to copy instance attribute deeply?

What should I do to copy session attribute deeply?

import copy

from requests_toolbelt.sessions import BaseUrlSession


class MyClass:
    def __init__(self, url):
        self.session = BaseUrlSession(base_url=url)


o = MyClass(url='https://httpbin.org')
print(o.session.base_url)  # outputs: https://httpbin.org

copied = copy.deepcopy(o)
print(copied.session.base_url)  # outputs: None

P.S. I know there is something to do with __deepcopy__ method.

Upvotes: 3

Views: 370

Answers (1)

Georgina Skibinski
Georgina Skibinski

Reputation: 13397

To directly answer your question - since you have no arguments in your constructor, and everything else is just deterministic - you can just add the following function to your class:

def __deepcopy__(self, memo):
    return MyClass()

For more elaborated case (where you have arguments on the constructor):

def __deepcopy__(self, memo):
    new_ = MyClass(None)
    for el in dir(self):
        if(el[:2]+el[-2:] != "____"):
            setattr(new_, el, getattr(self, el)) 
    return new_

Note - you can try recursively deepcopy every argument in the class, and then every argument of that argument (defininig it in a sensible manner would be non-trivial, since you should copy only arguments that weren't reinitialised in deep copy before) etc.

This would work, assuming all the arguments down the line have deepcopy implemented, as you like. In your case - BaseUrlSession already would fail this approach:

a = BaseUrlSession(base_url="http://www.google.pl")
b = copy.deepcopy(a)

print(a.base_url, b.base_url)
# prints: 
# http://www.google.pl None

Now the even more recommended approach would be to explicitly store all the variables, which will enable you to re-initialize your class to get proper deepcopy, so simply:

class MyClass:
    def __init__(self, url):
        self.url = url
        self.session = BaseUrlSession(base_url=url)

    def __deepcopy__(self, memo):
        return MyClass(url=self.url)

Upvotes: 2

Related Questions