python学习(28) 浅谈可变对象的单例模式设计
python开发,有时候需要设计单例模式保证操作的唯一性和安全性。理论上python语言底层实现和C/C++不同,python采取的是引用模式,当一个对象是可变对象,对其修改不会更改引用的指向,当一个对象是不可修改对象,对其修改会改变引用指向。
可变对象和不可变对象
不可变对象
该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象
该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。
python 中的可变对象和不可变对象
Python中,数值类型(int和float)、字符串str、元组tuple都是不可变类型。
而列表list、字典dict、集合set,以及开发人员自己定义的类是可变类型
python 和C/C++ 内存分配差异
- a = 2
- b = 2
- c = a + 0
- c += 0
- print(id(a), id(b), id(2)) # id都相同
- print(c is b) #True
python 中变量a,b,c都是常量2的引用,所以他们的地址空间都相同。在C/C++中,a,b,c是三个变量,每个变量地址都不一样,这一点大家在学习语言时要注意,这算是python的特性吧。同样的道理,字符串也是一样的
- astr = 'good'
- bstr = 'good'
- cstr = astr + ''
- print(cstr is bstr) # True
- print(id(astr), id(bstr), id('good')) # 三个id相同
字符串也是不可变对象,所以astr,bstr,cstr指向的都’good’所在空间
如果修改astr,则astr指向改变了
- astr = 'good'
- print(id(astr))
- astr += 'aa'
- print(id(astr)) # id和上面的不一样
因为str是不可变对象,当修改它的值后变为’goodaa’,那么astr指向的地址也改变为’goodaa’所在地址。id(astr)和之前的不一样了。
- lis = [1, 2, 3]
- lis2 = [1, 2, 3]
- # 虽然它们的内容一样,但是它们指向的是不同的内存地址
- print(lis is lis2)
- print(id(lis), id(lis2), id([1, 2, 3])) # 三个id都不同
虽然lis和lis2内容一样,但是可变对象都会单独开辟空间,所以上边三个id打印结果都不一样。
- alist = [1, 2, 3]
- # alist实际上是对对象的引用,blist = alist即引用的传递,现在两个引用都指向了同一个对象(地址)
- blist = alist
- print(id(alist), id(blist)) # id一样
- # 所以其中一个变化,会影响到另外一个
- blist.append(4)
- print(alist) # 改变blist, alist也变成了[1 ,2 ,3 4]
- print(id(alist), id(blist)) # id一样,和上面值没有改变时候的id也一样
blist赋值为alist,这两个指向同一个地址,当修改blist时,alist也改变了,所以打印两个id都是一样的。一般我们自己定义的类也是可变对象,我们想做的是通过设计一个单例类实现统一的控制,这样便于管理,比如网络模块,比如数据库处理模块等等。下面浅谈三种单例模式设计
python 单例模式设计
方法一:使用装饰器
装饰器维护一个字典对象instances,缓存了所有单例类,只要单例不存在则创建,已经存在直接返回该实例对象。
- def singleton(cls):
- instances = {}
- def wrapper(*args, **kwargs):
- if cls not in instances:
- instances[cls] = cls(*args, **kwargs)
- return instances[cls]
- return wrapper
- @singleton
- class Foo(object):
- pass
- foo1 = Foo()
- foo2 = Foo()
- print foo1 is foo2
singleton函数中定义了instances字典,当使用它作为装饰器装饰Foo后instances也会被缓存在闭包环境中,第一次使用Foo()后,instances就回被设置为instances[Foo]=Foo(),这样根据类名就可以区分是否被初始化过,从而实现单例模式
方法二:使用基类
new是真正创建实例对象的方法,所以重写基类的new方法,以此来保证创建对象的时候只生成一个实例
- class Singleton(object):
- def __new__(cls, *args, **kwargs):
- if not hasattr(cls, '_instance'):
- cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
- return cls._instance
- class Foo(Singleton):
- pass
- foo1 = Foo()
- foo2 = Foo()
- print foo1 is foo2 # True
new在一个类构造实例对象时会调用,所以通过判断hasattr,是否含有某个属性,即可实现单例模式。
super(Singleton,cls)调用的是Singleton的基类。我目前用的是这种方式实现的单例,用作网络和数据库管理。
方法三:使用元类
元类(参考:深刻理解Python中的元类)是用于创建类对象的类,类对象创建实例对象时一定会调用call方法,因此在调用call时候保证始终只创建一个实例即可,type是python中的一个元类
- class Singleton(type):
- def __call__(cls, *args, **kwargs):
- if not hasattr(cls, '_instance'):
- cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
- return cls._instance
- class Foo(object):
- __metaclass__ = Singleton
- foo1 = Foo()
- foo2 = Foo()
- print foo1 is foo2 # True
这种方式和new类似,都是通过系统级的函数call进行控制。通过在类中设置元类从而实现单例控制。
到目前为止,单例模式介绍完毕,感谢关注我的公众号
python学习(28) 浅谈可变对象的单例模式设计的更多相关文章
- TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN
前言 上一章为大家介绍过深度学习的基础和多层感知机 MLP 的应用,本章开始将深入讲解卷积神经网络的实用场景.卷积神经网络 CNN(Convolutional Neural Networks,Conv ...
- python进阶_浅谈面向对象进阶
python进阶_浅谈面向对象进阶 学了面向对象三大特性继承,多态,封装.今天我们看看面向对象的一些进阶内容,反射和一些类的内置函数. 一.isinstance和issubclass class F ...
- Python测试开发-浅谈如何自动化生成测试脚本
Python测试开发-浅谈如何自动化生成测试脚本 原创: fin 测试开发社区 前天 阅读文本大概需要 6.66 分钟. 一 .接口列表展示,并选择 在右边,点击选择要关联的接口,区分是否要登录, ...
- Python中为什么不能用可变对象作为默认参数的值
def func(numbers = [], num=1): numbers.append(num) for number in numbers: print(number) func() >& ...
- Python浅拷贝与深拷贝(可变对象与不可变对象)
第一次遇到深拷贝和浅拷贝的问题是用python在一个for循环中对一个list赋值,使用的语句是 a = b 这个b会不断带入循环,每次计算得到,最后发现list乱七八糟的,后来才发现,python中 ...
- python学习之路-day6-面向对象
一.面向对象学习 特性 class类 一个类即是对一类拥有相同属性的对象的抽象.蓝图.原型.在类中定义了这些对象的都具备的属性.共同的方法. 面向过程编程和面向对象编程: 面向过程编程:使用一系列的指 ...
- 关于AngularJS学习整理---浅谈$scope(作用域) 新手必备!
作为初次接触 AngularJS的新手,想要深层理解里面的内容短时间还是不可能的,所以标题写了浅谈字样,以下内容是参考各位大神以及相关书籍整理加个人理解,出现错误的地方请大家指正. $scope(作用 ...
- python函数默认参数为可变对象的理解
1.代码在执行的过程中,遇到函数定义,初始化函数生成存储函数名,默认参数初识值,函数地址的函数对象. 2.代码执行不在初始化函数,而是直接执行函数体. 代码实例 这要从函数的特性说起,在 Python ...
- 深度学习:浅谈RNN、LSTM+Kreas实现与应用
主要针对RNN与LSTM的结构及其原理进行详细的介绍,了解什么是RNN,RNN的1对N.N对1的结构,什么是LSTM,以及LSTM中的三门(input.ouput.forget),后续将利用深度学习框 ...
随机推荐
- Python基础(字符串和编码)
字符编码 我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特 ...
- POJ2531&&1416&&2676&&1129
搜索专题的最后一块了,也告别了这些老的东西了 接下来就是些全新的内容了啊! 这次的标签是简单搜索技巧和剪枝,也就是优化爆搜 当然,像Dancing links这样的玄学操作还是没有的 2531 题意: ...
- kvm虚拟化一: 图形化的管理方式
1.安装必要工具yum install -y / qemu-kvm //kvm主程序 libvirt //虚拟化服务库 libguestfs-tools //虚拟机系统管理工具 virt-instal ...
- linux查找进程pid并杀掉
命令:ps aux | grep `pwd` | grep -v grep | awk '{print $2}' | xargs kill -9 详细解释[我的有道云笔记,不知道为什么没法直接复制到 ...
- python中列表的常用操作增删改查
1. 列表的概念,列表是一种存储大量数据的存储模型. 2. 列表的特点,列表具有索引的概念,可以通过索引操作列表中的数据.列表中的数据可以进行添加.删除.修改.查询等操作. 3. 列表的基本语法 创建 ...
- Allegro怎么对元器件进行对齐
Allegro怎么对元器件进行对齐? Ø选择操作模式,点击菜单栏setup-Application Mode,然后选择Placement Edit模式,进行操作: Ø然后Find面板勾选器件选项,sy ...
- 【转】Cocos2d-x 3.x基础学习: 总结数学类Vec2/Size/Rect
转载:http://www.taikr.com/article/1847 在Cocos2d-x 3.x中,数学类Vec2.Size.Rect,是比较常用的类.比如设置图片位置,图片大小,两图片的碰撞检 ...
- cocos2d-x学习之路(一)——安装cocos2d-x
这两天想从pygame和SDL换到cocos2d-x上(主要还是为了跨平台开发),所以这里先来看看如何安装cocos2d-x. 首先到官网去下载cocos2d-x:传送门 点击上方菜单栏的Produc ...
- leetcode-递增的三元子序列
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列. 数学表达式如下: 如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,使得 arr[ ...
- mui框架(二)
1.底部导航切换界面 HTML部分: <nav class="mui-bar mui-bar-tab"> <a id="defaultTab" ...