我在Python 2.7中有以下原型模式的实现:
def clone (instance):
x = object.__new__ (type (instance))
x.__dict__ = dict (instance.__dict__)
return x
这显然不适用于非新式类(旧式类?)和内置插件如dict。
有没有办法,干净利落地保留在Python 2中,将其扩展为可变的内置类型,如序列和映射类型?
答案 0 :(得分:3)
我认为您可以使用copy module
deepcopy
方法创建对象的1-1副本:
>>> import copy
>>> x = [1,2,3]
>>> z = copy.deepcopy(x)
>>> x[0] = 3
>>> x
[3, 2, 3]
>>> z
[1, 2, 3]
答案 1 :(得分:2)
来自Mark Summerfield的书Python in practice 你可以创建对象的副本
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
使用代码的模式和示例,您可以在tutorialspoint上阅读,它适用于java,但原则是可理解的
答案 2 :(得分:1)
你想做的事情是错误的。
如果您只想要副本,只需使用copy
或deepcopy
。
如果你想要JavaScript风格的对象,可以创建一个可以克隆的根类,或者更好的是,用Python而不是JavaScript重新思考你的代码。
如果你想要内置的全功能JavaScript风格的克隆,你就不能拥有它。
另外,请记住,在基于非原型的语言(如Java和C ++)中使用原型模式的主要动机是(a)避免new
对象的成本和(b)允许向不同的实例添加不同的方法或属性。对于前者,你并没有避免成本,而且无论如何在Python中并不重要。对于后者,已经很容易在Python中向实例添加方法和属性,克隆不会以任何方式使它变得更容易。
我知道数字类型与其他内置类型有所不同......
不,这里的区别不是数字与序列,它是不可变类型与可变类型:
>>> id(tuple())
4298170448
>>> id(tuple())
4298170448
>>> id(tuple(()))
4298170448
>>> id(tuple([]))
4298170448
此外,它与int
或tuple
构造函数无关。如果值没有缓存在任何地方,即使重复的文字也会每次都获得一个新实例:
>>> id(20000)
4439747152
>>> id(20000)
4439747216
小整数,空元组以及不可命名的魔术常量的值在启动时预先缓存。短字通常被拘禁。但确切的细节依赖于实现。
那么,这对您有何影响?
好吧,克隆不可变类型毫无意义。根据定义,它是同一件事物的不可更改的副本,那么有什么用呢?
同时,不能以这种方式克隆不使用__dict__
的类型(无论它们是内置类型,使用插槽的类型,动态生成其属性的类型,......)。在某些情况下,您会收到错误,而在其他情况下则会出错。
因此,如果通过“序列,映射和数字类型”包含int
和list
等内置函数,则stdlib类型如Decimal
和deque
,或者常见的第三类 - 派对类型如gmpy.mpz
或blist.sorteddict
,则此机制将无效。
最重要的是,即使你可以克隆内置类,也无法向它们添加新属性:
>>> a = []
>>> a.foo = 3
AttributeError: 'list' object has no attribute 'foo'
所以,如果你再次使用它,那么无论如何都不会有用。
同时,调用object.__new__
而不是type(instance).__new__
会导致不同类别的各种问题。其中一些,例如__dict__
,会给你一个错误告诉你,但在每种情况下你都不能指望它。
这个想法存在其他不太严重的问题。例如:
>>> 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