Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a copy.deepcopy() clone function

Only for educational purposes, I'm trying to implement a clone function of copy.deepcopy().

After some fun playing with the code and googling around, I came up with the following function:

def my_deepcopy(data):

    if isinstance(data, dict):
        result = {}
        for key, value in data.items():
            result[key] = my_deepcopy(value)

        assert id(result) != id(data)

    elif isinstance(data, list):
        result = []
        for item in data:
            result.append(my_deepcopy(item))

        assert id(result) != id(data)

    elif isinstance(data, tuple):
        aux = []
        for item in data:
            aux.append(my_deepcopy(item))
        result = tuple(aux)

        assert id(result) != id(data)

    elif isinstance(data, (int, float, type(None), str, bool)):
        result = data
    else:
        raise ValueError("unexpected type")

    return result

It seems to work with all Python primitive types and its combinations:

# Various object types
lst_obj = [ 0, 1.1, 'foo', 'bar' ]
dict_obj = { 'zero' : 0, 'pi' : 3.1415, 'desc' : 'foobar' }
list_list_obj = [ [1,2,3], [4,5,6], [7,8,9] ]
tuple_list_obj = [ (-1,-1), (0,-1,0), (-1,0), (0,0,0,0) ]
dict_list_obj = [ {'zero' : 0}, {'pi' : 3.1415}, {'desc' : 'foobar'} ]

# Testing
my_deepcopy( lst_obj )        #OK!
my_deepcopy( dict_obj )       #OK!
my_deepcopy( list_list_obj )  #OK!
my_deepcopy( tuple_list_obj ) #OK!
my_deepcopy( dict_list_obj )  #OK!

So far, so good, but what about Arbitrary Types ? How do I duplicate the instance of an arbitrary object ? How do I detect it ? Do arbitrary types have any kind of copy constructor ?

What is missing in my function for the following code to work:

class Xpto:
    pass

arbitrary = [ Xpto(), Xpto() ]
my_deepcopy( arbitrary )     #ValueError("unexpected type")
like image 460
Lacobus Avatar asked Nov 08 '22 14:11

Lacobus


1 Answers

You can do some "Category Checking" for a better design of your function, searching for Sequence or Mapping trough the the __dict__ attribute of arbitrary objects:

>>> import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance((), collections.Sequence)
True
>>> isinstance('foo', collections.Sequence)
True
>>> isinstance({}, collections.Mapping)
True
>>> isinstance(23, collections.Sequence)
False
>>> isinstance(None, collections.Sequence)
False

Also check for the __class__ or __name__ attribute, to check if the object was instantiated from some class type that is not in your if statements for example:

def my_deepcopy(data):

    if isinstance(data, dict):
        result = {}
        for key, value in data.items():

            result[key] = my_deepcopy(value)

        assert id(result) != id(data)

    elif isinstance(data, list):
        result = []
        for item in data:
            result.append(my_deepcopy(item))

        assert id(result) != id(data)

    elif isinstance(data, tuple):
        aux = []
        for item in data:
            aux.append(my_deepcopy(item))
        result = tuple(aux)

        assert id(result) != id(data)

    elif isinstance(data, (int, float, type(None), str, bool)):
        result = data

    elif hasattr(data, '__name__'):#Its a function or a class
        auxObj = data.__class__() #Assuming it has not __init__ method defined
        auxDict = {}
        for key, value in data.__dict__:
            auxDict[key] = my_deepcopy(value)

        assert id(result) != id(data)

    else:
        raise ValueError("unexpected type")

    return result
like image 138
Alan Garrido Avatar answered Nov 14 '22 22:11

Alan Garrido