Python中的原型模式

时间:2013-07-12 08:01:58

标签: python design-patterns prototype-pattern

我在Python 2.7中有以下原型模式的实现:

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

这显然不适用于非新式类(旧式类?)和内置插件如dict。

有没有办法,干净利落地保留在Python 2中,将其扩展为可变的内置类型,如序列和映射类型?

3 个答案:

答案 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)

你想做的事情是错误的。

如果您只想要副本,只需使用copydeepcopy

如果你想要JavaScript风格的对象,可以创建一个可以克隆的根类,或者更好的是,用Python而不是JavaScript重新思考你的代码。

如果你想要内置的全功能JavaScript风格的克隆,你就不能拥有它。

另外,请记住,在基于非原型的语言(如Java和C ++)中使用原型模式的主要动机是(a)避免new对象的成本和(b)允许向不同的实例添加不同的方法或属性。对于前者,你并没有避免成本,而且无论如何在Python中并不重要。对于后者,已经很容易在Python中向实例添加方法和属性,克隆不会以任何方式使它变得更容易。


  

我知道数字类型与其他内置类型有所不同......

不,这里的区别不是数字与序列,它是不可变类型与可变类型:

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

此外,它与inttuple构造函数无关。如果值没有缓存在任何地方,即使重复的文字也会每次都获得一个新实例:

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

小整数,空元组以及不可命名的魔术常量的值在启动时预先缓存。短字通常被拘禁。但确切的细节依赖于实现。


那么,这对您有何影响?

好吧,克隆不可变类型毫无意义。根据定义,它是同一件事物的不可更改的副本,那么有什么用呢?


同时,不能以这种方式克隆不使用__dict__的类型(无论它们是内置类型,使用插槽的类型,动态生成其属性的类型,......)。在某些情况下,您会收到错误,而在其他情况下则会出错。

因此,如果通过“序列,映射和数字类型”包含intlist等内置函数,则stdlib类型如Decimaldeque,或者常见的第三类 - 派对类型如gmpy.mpzblist.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