Python中的原型模式



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

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

这显然不适用于非新型类(旧式类?)和dict.等内置类

有没有一种方法可以在Python 2中干净地将其扩展到可变的内置类型,如序列和映射类型?

我认为您可以使用复制模块

deepcopy方法创建对象的1-1副本:

>>> import copy
>>> x = [1,2,3]
>>> z = copy.deepcopy(x)
>>> x[0] = 3
>>> x
[3, 2, 3]
>>> z
[1, 2, 3]

它来自Mark Summerfield的《实践中的Python》一书您可以创建对象的副本

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,但原理是可理解的

你试图做的事情被误导了。

如果您只想要一个副本,只需使用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等内置类型、Decimaldeque等stdlib类型或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

最新更新