目标:
- 单例设计模式
- __new__方法
- Python中的单例
1.单例设计模式
- 设计模式:
- 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式,都是针对 某一特定问题 的成熟的解决方案
- 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
- 单例设计模式
- 目的——让 类 创建的对象,在系统中只有 唯一的一个实例
- 每一次执行 类名() 返回的对象,内存地址是相同的
- 单例设计模式的应用场景
- 音乐播放 对象
- 回收站 对象
- 打印机 对象
- ········
2.__new__方法
- 使用 类名() 创建对象时,Python解释器会先调用__new__方法为对象分配空间,再调用__init__()方法初始化对象
- __new__是由object基类提供的内置的静态方法,主要作用有两个
- 在内存中为对象分配空间
- 返回对象的引用
- Python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
1 class MusicPlayer(object):
2 def __new__(cls, *args, **kwargs):
3 print("创建对象,分配空间")
4
5 def __init__(self):
6 print("初始化对象")
7
8 player = MusicPlayer()
9 print(player)
10 '''
11 运行结果:
12 创建对象,分配空间 :可以看出在实例化对象时,先调用了new方法为对象开辟空间
13 None :因为重写了new方法,而new方法需要将对象的地址传递给init,
14 重写时并没有做,所以没有进行初始化
15 '''
View Code
* 重写__new__方法的代码非常的固定
1.
- 一定要 return super().new(cls)
- 否则Python解释器得不到分配了空间的引用,就不会调用对象的初始化方法
- 注意:__new__是一个静态方法,在调用时需要主动传递cls参数
-
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
#创建对象时,new方法会自动被调用
print("创建对象,分配空间")
#为对象分配空间
instance = super().__new__(cls)
#返回对象的引用
return instance
def __init__(self):
print("初始化对象")
player = MusicPlayer()
print(player)
'''
运行结果:
创建对象,分配空间
初始化对象
'''
- 单例——让类创建的对象,在系统中只有唯一的一个实例
- 定义一个 类属性,初始值是None,用于记录单例对象的引用
- 重写new方法
- 如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
- 返回类属性中记录的对象引用
1 #单例设计模式
2 class MusicPlayer(object):
3 instance = None #记录对象的地址
4 def __new__(cls, *args, **kwargs):
5 #判断对象是否为空,如果为空,就为其分配地址
6 if cls.instance is None:
7 #为对象分配空间
8 cls.instance = super().__new__(cls)
9 #返回对象的引用
10 return cls.instance
11 #如果部位空,就返回第一个对象的地址
12 else:
13 return cls.instance
14 def __init__(self):
15 pass
16
17 player_1 = MusicPlayer() #
18 print(player_1)
19
20 player_2 = MusicPlayer() #
21 print(player_2)
单例设计模式案例
- 只执行一次初始化动作
- 需求:让初始化动作只被执行一次
- 解决办法
- 定义一个类属性init_flag标记是否 执行过初始化动作,初始值为False
- 在 init 方法中,判断init_flag,如果为False就执行初始化动作
- 然后将init_flag设置为True
- 这样,再次调用__init__方法时,初始化动作就不会被执行了
1 #初始化动作只执行一次
2 class MusicPlayer(object):
3 instance = None #记录对象的地址
4 init_flag = False #标记是否执行过初始化动作
5 def __new__(cls, *args, **kwargs):
6 #判断对象是否为空,如果为空,就为其分配地址
7 if cls.instance is None:
8 #为对象分配空间
9 cls.instance = super().__new__(cls)
10 #返回对象的引用
11 return cls.instance
12 #如果部位空,就返回第一个对象的地址
13 else:
14 return cls.instance
15 def __init__(self):
16 #判断初始化动作是否执行过
17 if MusicPlayer.init_flag:
18 return
19 #如果没有执行过,那么执行初始化动作
20 print("初始化播放器")
21 #修改类属性(init_flag)的标记
22 MusicPlayer.init_flag = True
23
24 player_1 = MusicPlayer() #
25 print(player_1)
26 player_2 = MusicPlayer() #
27 print(player_2)
28 player_3 = MusicPlayer() #
29 print(player_3)
30 '''
31 运行结果:
32 初始化播放器
33 34 35 36 '''
只执行一次初始化动作
"""优化后的单例设计模式"""
import threading
class MusicPlayer(object):
# 设置标记
instance = None
init_flag = None
# 创建锁对象
lock = threading.RLock()
# 构造方法
def __new__(cls, *args, **kwargs):
if cls.instance:
return cls.instance
with cls.lock:
if not cls.instance:
cls.instance = object.__new__(cls)
return cls.instance
# 初始化方法,不过初始化方法通常不需要只初始化一次下面是演示如果需要初始化一次代码应该怎么写
def __init__(self, name):
# if cls.init_flag 这样子写代码会报错,cls只能在classmethod和new中才能获取到
if MusicPlayer.init_flag:
return
with self.lock:
if not MusicPlayer.init_flag:
self.name = name
MusicPlayer.init_flag = True
a = MusicPlayer("nihao1")
b = MusicPlayer("nihao2")
c = MusicPlayer("nihao9")
print(a.name, a)
print(b.name, b)
print(c.name, c)
Original: https://www.cnblogs.com/fjfsu/p/15458718.html
Author: J.FengS
Title: 单例设计模式
相关阅读
Title: Python趣味入门9:函数是你走过的套路,详解函数、调用、参数及返回值
琼恩·雪诺当上守夜人的司令后,为训练士兵对付僵尸兵团,把成功斩杀僵尸的一系列动作编排成了"葵花宝典剑法",这就是函数。相似,在计算机世界,一系列前后连续的计算机语句组合在一起称之为函数,本章主要介绍如何在程序中定义和使用函数。
有了函数后,当要用到相同功能时,我们就可以重复地调用它而不用重写代码。下面是琼恩·雪诺用来上报战况的程序,每杀死1鬼,通信员可以敲kill_dead(),而不用反复敲print()语句了。
如下定义了 kill_dead函数,用来上报战况
def kill_dead():
print('杀死异鬼1头')
上面def语句定义了函数kill_dead,函数名可以由写程序的人命名,必须以字母开头,紧跟圆括号,函数内的语句都必须多缩进4个空格。
例如print()是系统内置的函数,要显示文字时我们把文字放进括号里,这就是函数的参数。定义函数时我们也可以接受参数。
在调用时分为位置参数与关键字参数。
2.1 参数调用方式
我们来完善这个"战况通报程序",把英雄的名字和战斗业绩也加上。
如下定义了 kill_dead函数,用来上报战况
- name: 表示人名变量,
- number:表示战绩变量
def kill_dead(name,number):
print(name,'杀死异鬼',number,'头') #逗号分隔参数显示
这是主程序(没有缩进)
kill_dead('Snow',2) #位置参数调用
kill_dead(name='Sam',number = 1) #关键字参数调用
kill_dead('Guido',number = 1) #位置+关键字参数调用
运行上面的程序会显示出如下的结果:
Snow 杀死异鬼 2 头
Sam 杀死异鬼 1 头
Guido 杀死异鬼 1 头
第1行语句是位置调用,第2行语句是关键字调用方式,第3行是混合方式。
2.2 参数的默认值
上述播报战况的程序中,我们认为number(数量)每回填上比较麻烦,因为90%的的情况都是1,除了Snow偶尔会杀死2个。这我们就可以使用默认值,而不是每次都填。默认值可以使用等号写在参数变量后面,如下:
如下定义了 kill_dead函数,用来上报战况
- name: 表示人名变量,
- number:表示战绩变量
def kill_dead(name,number=1):
print(name,'杀死异鬼',number,'头') #显示
- 默认值数量为1,可以不填数量
kill_dead('小牛叔')
小牛叔杀死了1位,运行结果呢?
小牛叔 杀死异鬼 1 头
说明: 有默认值的参数变量,叫可选参数;没有默认值的参数,叫必选参数,可选参数定义时必须放在右侧。
2.3 参数的运算和*运算
特殊情况出现了,假设兵甲、兵乙...兵N同时杀死了1个敌人,这时的参数数量不固定怎么办?可以使用运算,把所有的位置参数"压缩"成一个元组变量;此外,还可以使用*运算,把所有的关键字参数"压缩"成一个字典。用法如下:
- *names: 基于位置参数元组,表示的人名变量,
- **killed:基于关键字参数的字典,表示目标和数量
def group_kill(*names,**killed):
print(names,'杀死',killed['target'],killed['number'],'头') #显示
张王被压缩成元组存在names,后面被组装进字典killed里。
group_kill('张','王',number=2,target='异龙')
运行结果:
('张', '王') 杀死 异龙 2 头
类似于input()输入函数可以返回用户输入的字符串结果,我们定义的函数也有返回值,返回值使用return语句。
假设有赏金猎人会根据杀敌多少从守夜人那里领取每个人头50金币每个龙500金币,计算2人3龙的赏金的程序这么写:
def money(dead:int,drag:int)->int:
return (dead*50 + drag*500)
运行如下:
print('2人3龙的赏金为:',money(2,3))
在本程序里,我们在参数后使用了:int,函数后使用了->int,其实这表示参数是整数,返回值是整数,加了会使程序更加好理解,不强制要求添加。
运行后的结果如下:
2人3龙的赏金为: 1600
Original: https://www.cnblogs.com/dosboy/p/dosboy_learn_python_9.html
Author: dosboy
Title: Python趣味入门9:函数是你走过的套路,详解函数、调用、参数及返回值