sugar/ 八月 15, 2019/ python设计模式/ 0 comments

下图展示了引用与副本之间的区别。

左侧是两个引用。Bob和Alice参考的是同一个食谱,这本质上意味着两者共享食谱,并且所有变更两人皆可见。右侧是同一个食谱的两个不同副本,这样允许各自独立地变更,Alice做的改变不会影响Bob做的改变,反之亦然。

原型设计模式(Prototype design pattern)帮助我们创建对象的克隆,其最简单的形式就是一个 clone() 函数,接受一个对象作为输入参数,返回输入对象的一个副本。在Python中,这可以使用 copy.deepcopy() 函数来完成。

下 图 展 示 了 细 胞 有 丝 分 裂 的 一 个 例 子

副本可以进一步分为深副本与浅副本。深副本就是我们在本章中到目前为止所看到的:原始对象的所有数据都被简单地复制到克隆对象中,没有例外。浅副本则依赖引用。我们可以引入数据共享和写时复制一类的技术来优化性能(例如,减小克隆对象的创建时间)和内存使用。如果可用资源有限(例如,嵌入式系统)或性能至关重要(例如,高性能计算),那么使用浅副本可能更佳。

在Python中,可以使用 copy.copy() 函数进行浅复制。以下内容引用自Python官方文档,说明 了 浅 副 本 ( copy.copy() ) 和 深 副 本 ( copy.deepcopy() ) 之 间 的 区 别:

  • 浅副本构造一个新的复合对象后,(会尽可能地)将在原始对象中找到的对象的引用插入新对象中。
  • 深副本构造一个新的复合对象后,会递归地将在原始对象中找到的对象的副本插入新对象中。
# coding: utf-8

import copy
from collections import OrderedDict


class Book:

    def __init__(self, name, authors, price, **rest):
        '''rest的例子有:出版商,长度,标签,出版日期'''
        self.name = name
        self.authors = authors
        self.price = price      # 单位为美元
        self.__dict__.update(rest)

    def __str__(self):
        mylist = []
        ordered = OrderedDict(sorted(self.__dict__.items()))
        for i in ordered.keys():
            mylist.append('{}: {}'.format(i, ordered[i]))
            if i == 'price':
                mylist.append('$')
            mylist.append('\n')
        return ''.join(mylist)


class Prototype:

    def __init__(self):
        self.objects = dict()

    def register(self, identifier, obj):
        self.objects[identifier] = obj

    def unregister(self, identifier):
        del self.objects[identifier]

    def clone(self, identifier, **attr):
        found = self.objects.get(identifier)
        if not found:
            raise ValueError('Incorrect object identifier: {}'.format(identifier))
        obj = copy.deepcopy(found)
        obj.__dict__.update(attr)
        return obj


def main():
    b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), price=118, publisher='Prentice Hall',
              length=228, publication_date='1978-02-22', tags=('C', 'programming', 'algorithms', 'data structures'))

    prototype = Prototype()
    cid = 'k&r-first'
    prototype.register(cid, b1)
    b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99,
                         length=274, publication_date='1988-04-01', edition=2)

    for i in (b1, b2):
        print(i)
    print('ID b1 : {} != ID b2 : {}'.format(id(b1), id(b2)))

if __name__ == '__main__':
    main()

原型模式用于创建对象的完全副本。确切地说,创建一个对象的副本可以指代以下两件事情。

  1. 当创建一个浅副本时,副本依赖引用
  2. 当创建一个深副本时,副本复制所有东西

第一种情况中,我们关注提升应用性能和优化内存使用,在对象之间引入数据共享,但需要小心地修改数据,因为所有变更对所有副本都是可见的。第二种情况中,我们希望能够对一个副本进行更改而不会影响其他对象。

Share this Post

说点什么

avatar
  Subscribe  
提醒