单例模式与原型模式

浏览 1402

课文

单例模式(Singleton Pattern)

单例模式非常容易理解:当你希望程序在运行时某个类有且仅有一个实例时,就可以使用单例模式。

python 的单例模式也很容易实现:

class Singleton:
    # __new__()方法是类的构造函数,先于__init_()_方法执行
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'): # 用hasattr()方法判断这个类是否已有实例
            cls._instance = super().__new__(cls)

        return cls._instance

一个简单的单例模式类就构造好了,如何判断它是否只有一个实例呢?我们知道,每实例化一次类,都会在内存空间中开辟新的储存空间用于存放这个实例,那么只需要多次实例化这个类,判断是否相等就知道了:

s1 = Singleton()
s2 = Singleton()
s3 = Singleton()

print(s1 == s2 == s3)

输出:

True

说明这个类在运行过程中只有一个实例。

原型模式(Prototype Pattern)

如果你拥有一个类的实例化对象,且希望获得若干个它的复制品,那么该如何实现呢?

python 里面提供了一个叫做copy的模块,其中两个方法copydeepcopy可以返回对象的浅拷贝和深拷贝,下面是示例代码:

from copy import copy, deepcopy

# 定义Student类,拥有一些属性和一个打印属性的方法
class Student:
    def __init__(self, name=None, age=None, grade=None, score=[]):
        self.name = name
        self.age = age
        self.grade = grade
        self.score = score

    def get_attr(self):
        print(self.name, self.age, self.grade, self.score)

score = [100, 100, 100]
# 创建一个Student类的对象t,初始化一些属性的值
t = Student('111', 7, 2, score)

# 分别对t进行浅拷贝和深拷贝
t_copy = copy(t)
t_deepcopy = deepcopy(t)

# 打印其内存地址与值
print(t)
print(t_copy)
print(t_deepcopy)
t.get_attr()
t_copy.get_attr()
t_deepcopy.get_attr()

输出:

<__main__.Student object at 0x000002BF42B57B80>
<__main__.Student object at 0x000002BF42B287C0>
<__main__.Student object at 0x000002BF42B63610>
111 7 2 [100, 100, 100]
111 7 2 [100, 100, 100]
111 7 2 [100, 100, 100]

这就是 python 里关于原型模式的使用方法。可以看到,三者之间的内存地址互不相同,但值是一样的,利用这两个方法我们可以快速获得一个类的多个对象。

那么copydeepcopy有什么区别呢?实际上copy只会进行浅拷贝,而deepcopy则会进行深拷贝。我们修改一下上面的代码:

from copy import copy, deepcopy

class Student:
    def __init__(self, name=None, age=None, grade=None, score=[]):
        self.name = name
        self.age = age
        self.grade = grade
        self.score = score

    def get_attr(self):
        print(self.name, self.age, self.grade, self.score)

score = [100, 100, 100]
t = Student('111', 7, 2, score)
t_copy = copy(t)
t_deepcopy = deepcopy(t)

score[1] = 99 # 这里是新增一段代码修改score的值
t.get_attr()
t_copy.get_attr()
t_deepcopy.get_attr()

输出:

111 7 2 [100, 99, 100]
111 7 2 [100, 99, 100]
111 7 2 [100, 100, 100]

我们会发现,t_copyscore同样被改变了,而t_deepcopy则没有改变。这里需要介绍一下 python 的内存机制:

  • 基本数据类型(str、int 等)储存在栈里,使用赋值操作符=时会重新复制一份:
a = 1
b = a   # 这里相当于b = 1
a = 2

print(b)

输出:

1
  • 而引用数据类型(list,dict 等)则会指向内存地址,改变它内部的数据并不会改变它的内存指向:
a = [1]
b = a       # 这里相当于将数组的内存地址同时赋值给了a和b
a[0] = 2    # 改变数组内部元素不会影响其内存地址

print(b)

输出:

[2]

从以上例子可以看出,deepcopy方法更接近于生活中我们所理解的「复制」概念。那为什么还要copy方法呢?实际上,进行深拷贝时会占用较多性能,而简单的业务场景进行深拷贝会显得有些多余,于是采取了浅拷贝方式去节约性能。

评论

登录参与讨论

暂无评论

共 0 条
  • 1
前往
  • 1