创建型--克隆模式

什么是克隆模式


克隆模式: 也称之为 原型模式,顾名思义,就是创建一个实例对象的副本。创建副本的过程中,分为浅副本深副本两种情况。

  • 浅副本: 浅副本与原对象的某些属性共用一片内存接口,这是一种引用操作;
  • 深副本: 深副本的所有属性内存与原对象的所有属性接口不一样,深副本的数据拷贝自原对象;

具体区别如下所示:

在这里插入图片描述

什么时候使用浅副本:如果资源有限(例如嵌入式系统)或者性能至关重要(例如高性能计算),使用浅复制 会更好,它可以实现数据共享、减少克隆对象的创建时间;
什么时候使用深副本:我们需要一个对象的完整副本。


为什么不选择重新创建一个实例对象,而选择克隆模式


  • 如果重新创建一个实例对象,我们得到的只是一个原始状态的实例对象;
  • 克隆模式获取的是当前状态下实例对象的副本;
  • 两者操作获取的实例对象的状态信息是不一样的。

克隆模式的应用场景


  • 当我们已有一个对象,并希望创建该对象的一个完整副本时,原型模式就派上用场了;
  • 当我们想复制一个复杂对象时,使用原型模式会很方便;避免了重新创建对象,并对数据库进行多次查询操作;

克隆模式的例子


例子1:深度复制:deepcopy函数

Python 有专门的库函数来实现深度复制,例如:copy.deepcopy()

import copy

class A:
    def __init__(self):
        self.x = 18
        self.msg = 'Hello'

class B(A):
    def __init__(self):
        super().__init__()
        self.y = 34

    def __str__(self):
        return '{}, {}, {}'.format(self.x, self.msg, self.y)

if __name__ == '__main__':
    b = B()
    c = copy.deepcopy(b)
    print([str(i) for i in (b, c)]) # 打印属性数据
    print([i for i in (b, c)]) # 打印实例地址

输出的结果:

['18, Hello, 34', '18, Hello, 34']
[<__main__.B object at 0x7f4764bdbcf8>, <__main__.B object at 0x7f4764bdbda0>]

例子2:克隆书籍

假设某本书第一版的信息包括:书名,作者,价格等。现在要对该书进行再版,新版和旧版之间绝大部分的信息都是一样的。一个很好的办法就是对第一版的信息进行克隆,在副本的基础之上进行修改。


定义书籍类

在下述代码中,Book类展示了一种有趣的技术可避免可伸缩构造器问题。在 __init__() 方法中,仅有三个形参是固定的: name、 authors、price,但是使用 rest 变长列表,调用者能以关键词的形式(名称=值)传入更多的参数。 self.__dict__.update(rest) 一行将 rest 的内容添加到 Book 类的内部字典中,成为它的一部分。
代码中还使用了 OrderedDict 来强制元素有序。

**加载公共库**
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)

定义克隆类

Prototype 类实现了原型设计模式。 Prototype类的核心是clone()方法,该方法使用我们熟悉的copy.deepcopy()函数来完成真正的克隆工作。但Prototype类在支持克隆之外做了一点更多的事情,它包含了方法 register()unregister(),这两个方法用于在一个字典中追踪被克隆的对象。

# 克隆类
class Prototype:

    def __init__(self):
        self.objects = dict() # 存储对象 

    # 登记要克隆的对象
    def register(self, identifier, obj):
        """
        Params:
            identifier:需要克隆对象的标识;
            obj:需要克隆的对象
        """
        self.objects[identifier] = obj

    # 在克隆集合中删除指定对象
    def unregister(self, identifier):
        del self.objects[identifier]

    # 返回克隆后的对象
    def clone(self, identifier, **attr):
        """
        Params:
            - identifier:对象标识;
            - **attr:需要被更新的属性以及属性数据
        """
        found = self.objects.get(identifier) # 根据标识获取对象
        if not found:
            raise ValueError('Incorrect object identifier: {}'.format(identifier))
        obj = copy.deepcopy(found) # clone 对象
        obj.__dict__.update(attr) # 更新副本的参数
        return obj

实现克隆操作

  • 首先建立了一个书籍对象 b1;
  • 把 b1 登记进入克隆实例对象中;
  • 选择克隆书籍对象 b1;
  • 对比 b1 和其副本的 id;
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()

运行的结果如下:

结果:
authors: ('Brian W. Kernighan', 'Dennis M.Ritchie')
length: 228
name: The C Programming Language
price: 118$
publication_date: 1978-02-22
publisher: Prentice Hall
tags: ('C', 'programming', 'algorithms', 'data structures')

authors: ('Brian W. Kernighan', 'Dennis M.Ritchie')
edition: 2
length: 274
name: The C Programming Language(ANSI)
price: 48.99$
publication_date: 1988-04-01
publisher: Prentice Hall
tags: ('C', 'programming', 'algorithms', 'data structures')

ID b1 : 139993865162592 != ID b2 : 139993865255400

源码在这里;


参考


  1. 《精通Python设计模式》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值