1

I have the following implementation of the prototype pattern in Python 2.7:

def clone (instance):
    x = object.__new__ (type (instance))
    x.__dict__ = dict (instance.__dict__)
    return x

This clearly doesn't work for non new-style classes (old style classes?) and built-ins like dict.

Is there a way, cleanly and staying within Python 2 to extend this to mutable built in types like sequence and mapping types?

3 Answers 3

4

I think that you can use the copy module

The deepcopy method creates 1-1 copy of an object:

>>> import copy
>>> x = [1,2,3]
>>> z = copy.deepcopy(x)
>>> x[0] = 3
>>> x
[3, 2, 3]
>>> z
[1, 2, 3]
Sign up to request clarification or add additional context in comments.

2 Comments

I wasn't aware of that module and a review of the source code was illuminating. Thank you. On reflection, it makes no sense to provide a clone of simple immutable types - like numeric types. The deepcopy function has special cases for built in types to handle these.
@Owen That's right, but it's worthy to remember that deepcopy could be slower than directly coding because of this special cases - check that writeonly.wordpress.com/2009/05/07/…
4

it from book Python in practice Book by Mark Summerfield you can create copy of object

class Point:
    __slots__ = ("x", "y")
   def __init__(self, x, y):
      self.x = x
      self.y = y

def make_object(Class,*args,**kwargs):
    return Class(*args,**kwargs)

point1 = Point(1, 2)
point2 = eval("{}({}, {})".format("Point", 2, 4)) # Risky
point3 = getattr(sys.modules[__name__], "Point")(3, 6)
point4 = globals()["Point"](4, 8)
point5 = make_object(Point, 5, 10)
point6 = copy.deepcopy(point5)
point6.x = 6
point6.y = 12
point7 = point1.__class__(7, 14) # Could have used any of point1 to point6

schema and example of use code you can read on tutorialspoint, it for java, but principle is comprehensible

Comments

2

What you're trying to do is misguided.

If you want just want a copy, just use copy or deepcopy.

If you want JavaScript-style objects, create a root class that you can clone—or, better, rethink your code in Python instead of JavaScript.

If you want fully-functional JavaScript-style cloning even for builtins, you can't have that.

Also, keep in mind that the main motivations for using the prototype pattern in non-prototype-based languages like Java and C++ are (a) to avoid the cost of newing up an object and (b) to allow adding different methods or attributes to different instances. For the former, you're not avoiding the cost, and it doesn't matter anyway in Python. For the latter, it's already easy to add methods and attributes to instances in Python, and cloning doesn't make it any easier in any way.


I'm aware that there is something different going on with numeric types, compared with other built in types…

No, the distinction here isn't numbers vs. sequences, it's immutable types vs. mutable:

>>> id(tuple())
4298170448
>>> id(tuple())
4298170448
>>> id(tuple(()))
4298170448
>>> id(tuple([]))
4298170448

Also, it has nothing to do with the int or tuple constructor being fancy. If the value isn't cached anywhere, even a repeated literal will get a new instance each time:

>>> id(20000)
4439747152
>>> id(20000)
4439747216

Small integers, the empty tuple, and the values of the un-assginable magic constants are pre-cached at startup. Short strings generally get interned. But the exact details are implementation-dependent.


So, how does this affect you?

Well, cloning immutable types is pointless. By definition, it's an unchangeable copy of the same thing, so what good can that do?


Meanwhile, types that don't use __dict__ for their storage can't be cloned in this way (whether they're builtin types, types that use slots, types that generate their attributes dynamically, …). In some cases you'll get an error, in others just the wrong behavior.

So, if by "sequence, mapping and numeric types" you include builtins like int and list, stdlib types like Decimal and deque, or common third-party types like gmpy.mpz or blist.sorteddict, then this mechanism will not work.


On top of that, even if you could clone builtin classes, you can't add new attributes to them:

>>> a = []
>>> a.foo = 3
AttributeError: 'list' object has no attribute 'foo'

So, if you got this to work, again, it wouldn't be useful anyway.


Meanwhile, calling object.__new__ instead of type(instance).__new__ can cause a variety of problems for different classes. Some of them, like __dict__, will give you an error telling you so, but you can't count on that in every case.


There are other, less serious, problems with the idea. For example:

>>> class Foo(object):
...    def __init__(self):
...        self.__private = 3
>>> foo = Foo()
>>> foo.__private
3
>>> bar = clone(foo)
>>> bar.__private
AttributeError: 'Foo' object has no attribute '__private'
>>> bar._Foo__private
3

Comments

Your Answer

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.