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),后续将利用深度学习框 ...
随机推荐
- 几种flash存储芯片的用途和分类
1.IIC EEPROM------容量小,采用的是IIC通信协议:用于在掉电时,存系统配置参数,比如屏幕亮度等.常用芯片型号有 AT24C02.FM24C02.CAT24C02等,其常见的封装多为D ...
- Linux下的openvpn配置 与 easy-rsa3的证书生成
#注意:以下操作由服务端操作即可#PS:为什么我找不到var文件??============安装===============wget -O /etc/yum.repos.d/epel.repo ht ...
- [CF1067D]Computer Game[凸包/斜率优化+倍增+矩阵乘法]
题意 你有 \(n\) 个任务,初始收益为 \(a\) ,共 \(t\) 轮游戏,每轮可以选择完成一个任务(可以做多次),完成之后可以给任意任务升级,升级之后的任务收益为 \(b\) ,每个任务还有完 ...
- centos 7 jenkins 部署
安装jenkins 1.拉取库的配置到本地对应文件 sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redha ...
- Windows Server 2003出现Directory Listing Denied This Virtual Directory does not allow contents to be listed.的解决方案
Directory Listing DeniedThis Virtual Directory does not allow contents to be listed. 是目录权限无法访问的问题 解决 ...
- OPPO A7X 刷机小结
OPPO A7X 刷机小结: 概述:根据网上找到的教程(MTK模式刷机教程),没有成功.在QQ上询问一位提供刷机服务的大神,说是只有老版本才能刷. 操作步骤: 刷机工具: MediaTek SP Fl ...
- 记录:tf.saved_model 模块的简单使用(TensorFlow 模型存储与恢复)
虽然说 TensorFlow 2.0 即将问世,但是有一些模块的内容却是不大变化的.其中就有 tf.saved_model 模块,主要用于模型的存储和恢复.为了防止学习记录文件丢失或者蠢笨的脑子直接遗 ...
- GitHub 新手教程 一,GitHub 注册
1,注册地址: https://github.com/ 2,输入账号.邮箱.密码: 3,选择 Free 免费账号: 4,选择一些基本信息(翻译后中文见下面的图): 翻译如下: 5,打开你注册用的邮箱, ...
- 关于k8s这项大动作,预示着边缘计算迎来“开源”发展的新周期……
在文章<最近在边缘计算领域,发生了一件足以载入物联网史册的大事…>我曾经提到Kubernetes(简称K8s)将从超大规模云计算环境,被带入到物联网边缘计算场景中. 事情有了新进展,从本周 ...
- 用HackRF和Gqrx来听FM广播
本文内容.开发板及配件仅限用于学校或科研院所开展科研实验! 淘宝店铺名称:开源SDR实验室 HackRF链接:https://item.taobao.com/item.htm?spm=a1z10.1- ...