Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

copy.deepcopy raises TypeError on objects with self-defined __new__() method

I want to implement a symbol type, which keeps track of the symbols we already have(saved in _sym_table), and return them if they exist, or create new ones otherwise. The code:

# -*- coding: utf-8 -*-

_sym_table = {}

class Symbol(object):
    def __new__(cls, sym):
        if sym not in _sym_table:
            return super().__new__(cls)
        else:
            return _sym_table[sym]

    def __init__(self, sym):
        self.sym = sym
        _sym_table[sym] = self

    def __str__(self):
        return self.sym

    def __cmp__(self, other):
        return self is other

    def __hash__(self):
        return self.sym.__hash__()

But when I call copy.deepcopy on a list of such Symbol instances, exception is raised:

a = Symbol('a')
b = Symbol('b')
s = [a, b]
t = copy.deepcopy(s)

Error messages:

Traceback (most recent call last):
  File "xxx.py", line 7, in <module>
    t = copy.deepcopy(s)
  File "/usr/lib/python3.2/copy.py", line 147, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.2/copy.py", line 209, in _deepcopy_list
    y.append(deepcopy(a, memo))
  File "/usr/lib/python3.2/copy.py", line 174, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "/usr/lib/python3.2/copy.py", line 285, in _reconstruct
    y = callable(*args)
  File "/usr/lib/python3.2/copyreg.py", line 88, in __newobj__
    return cls.__new__(cls, *args)
TypeError: __new__() takes exactly 2 arguments (1 given)

So my questions are:

  • How can I make a deep copy on these objects with self-defined __new__ methods?
  • And any suggestions about when and how to use copy.deepcopy?

Thanks a lot!

like image 899
pjhades Avatar asked May 16 '12 12:05

pjhades


People also ask

How do you distinguish between copy copy () and copy Deepcopy ()?

copy() create reference to original object. If you change copied object - you change the original object. . deepcopy() creates new object and does real copying of original object to new one. Changing new deepcopied object doesn't affect original object.

How do you Deepcopy an object in Python?

To make a deep copy, use the deepcopy() function of the copy module. In a deep copy, copies are inserted instead of references to objects, so changing one does not change the other.

What is shallow copy and Deepcopy in Python?

3 Things to Remember. Making a shallow copy of an object won't clone child objects. Therefore, the copy is not fully independent of the original. A deep copy of an object will recursively clone child objects. The clone is fully independent of the original, but creating a deep copy is slower.

What is difference between deep copy and shallow copy?

In Shallow copy, a copy of the original object is stored and only the reference address is finally copied. In Deep copy, the copy of the original object and the repetitive copies both are stored.


2 Answers

one problem is that deepcopy and copy have no way of knowing which arguments to pass to __new__, therefore they only work with classes that don't require constructor arguments.

the reason why you can have __init__ arguments is that __init__ isn't called when copying an object, but __new__ must be called to create the new object.

so if you want to control copying, you'll have to define the special __copy__ and __deepcopy__ methods:

def __copy__(self):
    return self

def __deepcopy__(self, memo):
    return self

by the way, singletons are evil and not really needed in python.

like image 54
mata Avatar answered Oct 29 '22 08:10

mata


Seems to me you want the Symbol instances to be singletons. Deepcopy, however is supposed to be used when you want an exact copy of an instance, i.e. a different instance that is equal to the original.

So the usage here kinda contradicts the purpose of deepcopy. If you want to make it work anyhow, you can define the __deepcopy__ method on Symbol.

like image 37
XORcist Avatar answered Oct 29 '22 07:10

XORcist