本次博客更新的是python基础篇的最后一章,也是相当重要的一章——面向对象。然后我们即将进入python进阶篇的学习。
本次篇幅较长,因为及其重要,希望耐心观看!!!
在了解什么是面向对象之前,我们需要记住一句话:python一切皆对象
面向对象编程的思想主要是针对大型的软件设计的,面向对象使得程序扩展性更强,可读性更好。
面向对象编程将数据和操作数据的方法封装到了对象中,组织代码和数据的方式更灵活,python是真正的面向对象的编程语言,完全支持面向对象的基本功能:继承、多态、封装。
python支持面向对象,也包含了面向过程和函数式编程,它是集成多种编程方式的编程范式
随着编程面临的问题越来越复杂,编程语言本身也在进化,从主要处理简单的数据开始,随着数据变多进化有了数组;数据类型变复杂进化出了结构体;处理数据的方式和逻辑变复杂进化出了对象。
struct resume {
int age;
char name[10];
double t;
};
class Student:
school = “小学” #类属性
score = 100 #类属性
def __init__(self,name,age):
self.name = name #实例属性
self.age = age
Student.score = Student.count + 1
def hello(self): #实例方法
print("我的学校是:",Student.school)
print(slef.name,"的年龄是:",self.age)
前面我们了解过的数据类型,如整数也是一个对象,是一个包含了加法。乘法等方法的对象
对象是类的实例,类是对象的抽象
我们通过类定义数据类型的属性和方法,也就是说:类将行为和状态打包在一起。
对象是累的具体实现,一般称为类的实例。
从一个类创建对象时,每个对象都会共享这个类的行为(类中定义的方法),也会有自己的属性值。也就是说:方法代码是共享的,属性数据不是。
python中一切皆对象。类也被称为类对象,类的实例称为实例对象。
定义类的语法格式:
class 类名:
类体
注意:
【操作】类的定义:
class Student:
def __init__(self,name,score): #构造方法第一个参数必须是self
self.name = name #实例属性
self.score = score #
def tell(self): #实例方法
print(self.name,"的分数是:",self.score)
s1 = Student("shy-2",100) #s1是实例对象 自动调用__init__()方法
s1.tell()
类是抽象的,也被称为对象的模版。我们需要通过类这个模版创建类的实例对象,然后才能使用类定义的功能
python对象包含三个部分,前面博客提到过:id(identity 识别码)、type(对象类型)、value(对象的值)
现在我们可以精确的说,一个python对象包含以下部分:
创建对象,我们需要定义构造函数__init__()。构造函数用于执行实例对象的初始化工作,即对象创建后初始化当前对象的相关属性,无返回值。
__ init__ ()注意事项:
def __init__(self,name,score):
self.name = name #实例属性
self.score = score
类名(参数列表)
来调用构造函数,调用后,将创建好的对象返回给相应的变量__ init__()和 __ new__():
__ init__()方法:初始化创建好的对象,初始化指的是:给实例属性赋值
__ new__()方法:用于创建对象,我们不需要重新定义该方法
如果我们不定义构造函数,系统会提供一个默认的构造方法
实例属性是从属于实例对象的属性,也叫做实例变量,它的使用有以下要点:
self.实例属性名 = 初始值
self.实例属性名
实例方法是从属于实例对象的方法,实例方法定义格式:
def 方法名(self,[形参列表]):
函数体
方法的调用格式如下:对象.方法名([实参列表])
注意:
其他的操作:
在类定义:class 类名:
,当解释器执行class语句时,就会创建一个类对象
【操作】创建类对象:
class Student:
pass #空语句
print(type(Student))
print(id(Student))
S = Student
stu = S()
print(stu)
执行结果:
<class 'type'>
2660071558648
<__main__.Student object at 0x0000026B593619E8>
可以看到生成了一个变量名就是类名Student的对象。我们可以通过赋值给新变量S,也能实现相关的调用
类属性从属于类对象的属性,也是称为类变量、由于类属性是从属于类对象,可以被所有的实例对象共享
类属性的定义方式:
class 类名:
类变量名 = 初始值
在类中或者类的外面我们可以通过:类名.类变量名来调用
【操作】类属性的使用:
class Student:
school = "aabb" #类属性
score = 10 #类属性
def __init__(self,name,age):
self.name = name #实例属性
self.age = age
Student.score = Student.score+10
def say_age(self): #实例方法
print("我的学校是:",Student.school)
print(self.name,"年龄是:",self.age)
s1 = Student("张三",10) #s1是实例对象,自动调用__init__()方法
s1.say_age()
print("一共创建{0}个Student对象".format(Student.score))
我的学校是: aabb
张三 年龄是: 10
一共创建20个Student对象
类方法是从属于类对象的方法,类方法通过装饰器@classmethod来定义,格式:
@classmethod
def 类方法名(cls[,形参列表]):
函数体
要点:
【操作】类方法使用:
class Student:
company = "xixi" #类属性
@classmethod
def printCompany(cls):
print(cls.company)
Student.printCompany()
python中允许定义与类对象无关的方法称为:静态方法
静态方法和在模块中定义的普通函数没有区别,只不过静态方法放到了类的名字空间里面需要类才能调用
静态方法通过装饰器@staticmethod来定义,格式:
@staticmethod
def 静态方法名([形参列表]):
函数体
注意:
【操作】静态方法的使用:
class Student:
company = 'xixi' #类属性
@staticmethod
def add(a,b): #静态方法
print("{0}+{1}={2}".format(a,b,(a+b)))
return a+b
Student.add(10,10)
class Student:
company = 'xixi' #类属性
count = 0 #类属性
def __init__(self,name,score):
self.name = name #实例属性
self.score = score
Student.count = Student.count +1
def st_score(self): #实例方法
print("我的公司是:",Student.company)
print(self.name,"的分数是:",self.score)
s1 = Student("shy-2",22) #s1时实例对象 自动调用构造方法
s1.st_score()
print("一共创建{0}个Student对象".format(Student.count))
执行结果:
我的公司是: xixi
shy-2 的分数是: 22
一共创建1个Student对象
__del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。
Python实现自动的垃圾回收,当对象没有被引用时(引用计数为 0),由垃圾回收器 调用__del__方法。
我们可以通过del语句删除对象,从而保证调用__del__方法
系统会自动提供__del__方法,一般不需要自定义析构方法。
# 析构函数
class Person:
def __del__(self):
print("销毁对象:{0}".format(self))
p1 = Person()
p2 = Person()
del p2
print("程序结束")
执行结果:
销毁对象:<__main__.Person object at 0x0000011CB59BD748>
销毁对象:<__main__.Person object at 0x0000011CB59BD518>
程序结束
定义了__call__方法的对象称为可调用对象,即该对象可以像函数一样被调用
# 测试__call__ 可调用对象
class SalaryAccount:
"""工资计算类"""
def __call__(self,salary):
yearSalary = salary*12
daySalary = salary//30
hourSalary = daySalary//8
return dict(monthSalary=salary,yearSalary=yearSalary,daySalary=daySalary ,hourSalary=hourSalary)
s = SalaryAccount()
print(s(5000))# 可 以 像 调 用 函 数 一 样 调 用 对 象 的 _ _ c a ll _ _ 方 法
执行结果:
{'monthSalary': 5000, 'yearSalary': 60000, 'daySalary': 166, 'hourSalary': 20}
在其他的编程语言中,可以定i多个重名的方法,只要保证方法签名唯一即可。方法签名包含三个部分:方法名、参数数量、参数类型
python中,方法的参数没有声明类型(只有在调用时才能确定参数的类型),参数的数量也可以由可变参数控制,所以python中是没有方法的重载的,定义一个方法即可有多种调用方式,相当于其他编程语言的方法重载。
如果我们在类体中定义了多个重名的方法,只有最后一个有效
最好不要使用重名的方法,python中没有重载
# python中没有方法的重载 定义多个同名的方法 只有最后一个有效
class Person:
def say_s(self):
print("hello")
def say_s(self,name):
print("{0},hello".format(name))
p1 = Person()
# p1.say_s() #不带参报错:TypeError: say_s() missing 1 required positional argument: 'name'
p1.say_s("shy-2")
执行结果:
shy-2,hello
python是动态语言,我们可以动态的为类添加上新的方法或者动态的修改类已有的方法。
# 测试方法的动态性
class Person:
def work(self):
print("努力上班")
def play_game(self):
print("{0}玩游戏".format(self))
def work2(s):
print("好好工作,努力上班")
Person.play = play_game
Person.work = work2
p = Person()
p.play()
p.work()
执行结果:
<__main__.Person object at 0x0000011CB5A802B0>玩游戏
好好工作,努力上班
可以看到Person动态的新增了play_game方法以及 work2替换了原有的work方法
Python对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有 属性和私有方法,有如下要点:
注意:
方法本质上也是属性!只不过是可以通过()执行而已。所以,此处讲的私有属性和公 有属性,也同时讲解了私有方法和公有方法的用法。
【操作】:私有属性和公有属性的使用:
# 测试私有属性 私有方法
class Employee:
__company = "dddd" #私有类属性 通过dir可以查到_Employee__company
def __init__(self,name,age):
self.name = name
self.__age = age # 私 有 实 例 属 性
def say_company(self):
print("我的公司是:",Employee.__company) #类内部可以直接访问私有属性
print(self.name,"年龄是:",self.__age)
self.__work()
def __work(self):
# 私有实例方法 通过dir可以查到_Employee__cork
print("工作赚钱")
p1 = Employee("shy-2",22)
print(p1.name)
print(dir(p1))
p1.say_company()
print(p1._Employee__age) # 通 过 这 种 方 式 可 以 直 接 访 问 到 私 有 属 性 。 通 过 d ir 可 以 查 到 属 性 : _ E m p l o y e e _ _ a g e
# print(p1.__age) # 直 接 访 问 私 有 属 性 , 报 错
# p1.__sleep() # 直 接 访 问 私 有 方 法 , 报 错
p1._Employee__work()
执行结果:
shy-2
['_Employee__age', '_Employee__company', '_Employee__work', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_company']
我的公司是: dddd
shy-2 年龄是: 22
工作赚钱
22
工作赚钱
@property 可以将一个方法的调用方式变成“属性调用”。
# 测试@property
class Employee:
@property
def salary(self):
return 3000
emp1 = Employee()
print(emp1.salary) #打印:3000
print(type(emp1.salary)) #打印:
# emp1.salary() #报错: 'int' object is not callable
emp1.salary = 2000 #此处修改报错:AttributeError: can't set attribute
# @ p r o p e r t y 修 饰 的 属 性 , 如 果 没 有 加 s e t t e r 方 法 , 则 为 只 读 属 性
@property 主要用于帮助我们处理属性的读操作、写操作。对于某一个属性,我们可以直接通过:
emp1.salary = 30000
如上的操作读操作、写操作。但是,这种做法不安全。比如,我需要限制薪水必须为1-10000 的数字。这时候,我们就需要通过getter、setter方法来处理。
class Employee:
def __init__(self,name,salary):
self.name = name
self.__salary = salary
@property #相当于salary属性的getter方法
def salary(self):
print("月薪为{0},年薪为{1}".format(self.__salary,(12*self.__salary)))
return self.__salary
@salary.setter
def salary(slef,salary): #相当于salary属性的setter方法
if(0<salary<1000000):
self.__salary = salary
else:
print("薪水录入错误!只能在0~1000000之间")
emp1 = Employee("shy-2",2000)
print(emp1.salary)
emp1.salary = -2000
执行结果:
月薪为2000,年薪为24000
2000
薪水录入错误!只能在0~1000000之间
Python是面向对象的语言,也支持面向对象编程的三大特性:继承、封装(隐藏)、多态。
继承:
继承可以让子类具有父类的特性,提高了代码的重用性。
从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进 已有的算法。
封装:
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。
通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python 追求简洁的 语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现
多态:
多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆 是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是敲代码。
继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。 如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作 难度。已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”。
Python支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:
class 子类类名(父类1[,父类2,……]):
类体
如果在类定义中没有指定父类,则默认父类是object 类。也就是说,object 是所有类的父 类,里面定义了一些所有类共有的默认实现,比如:__new __()。
定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下:
父类名.__init__(self,参数列表)
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
def say_age(self):
print(self.name,"的年龄是:",self.__age)
class Student(Person):
def __init__(self,name,age,score):
self.score = score
Person.__init__(self,name,age)
s1 = Student("shy-2",22,100)
s1.say_age()
print(dir(s1))
执行结果:
shy-2 的年龄是: 22
['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_age', 'score']
成员继承:子类继承了父类除构造方法之外的所有成员。
方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写
【操作】继承和重写:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def say_age(self):
print(self.name,"的年龄是:",self.age)
def say_name(self):
print("我是",self.name)
class Student(Person):
def __init__(self,name,age,score):
self.score = score
Person.__init__(self,name,age) #构造函数中包含父类构造函数
def say_score(self):
print(self.name,"的分数是:",self.score)
def say_name(self): #重写父类的方法
print("报告老师,我是",self.name)
s1 = Student("shy-2",22,100)
s1.say_score()
s1.say_name()
s1.say_age()
执行结果:
shy-2 的分数是: 100
报告老师,我是 shy-2
shy-2 的年龄是: 22
通过类的方法mro或者类的属性__mro__可以输出这个类的继承层次结构
【操作】查看类的继承层次结构:
class A:pass
class B(A):pass
class C(A):pass
print(C.mro())
# print(C.__mro__)
执行结果:
[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
object 类是所有类的父类,因此所有的类都有object 类的属性和方法。我们显然有必要深 入研究一下object 类的结构。对于我们继续深入学习Python 很有好处。
dir()查看对象属性:
为了深入学习对象,我们先学习内置函数 dir(),他可以让我们方便的看到指定对象所有的 属性。
【操作】查看对象所有的属性以及和object进行比较:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def say_age(self):
print(self.name,"的年龄是:",self.age)
obj = object()
print(dir(obj))
print('*'*20)
s1 = Person("shy-2",22)
print(dir(s1))
执行结果:
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
********************
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age']
从执行结果中可以看出:
重写__str__()方法:
object 有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数 str() 经常用于 print()方法,帮助我们查看对象的信息。__str __()可以重写。
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age
def __str__(self):
'''将对象转化为一个字符串,一般用于print方法'''
return "名字是:{0},年龄是{1}".format(self.name,self.__age)
p = Person("shy-2",18)
print(p)
执行结果:
名字是:shy-2,年龄是18
Python支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父 类”的特点。但是由于,这样会被“类的整体层次”搞的异常复杂,尽量避免使用。
Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将 “从左向右”按顺序搜索。
MRO(MethodResolution Order):方法解析顺序。 我们可以通过mro()方法获得 “类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
class A:
def aa(self):
print("aa")
def say(self):
print("say AAA")
class B:
def bb(self):
print("bb")
def say(self):
print("say BBB")
class C(B,A):
def cc(self):
print("cc")
c = C()
print(C.mro()) #打印类的层次结构
c.say() # 解释器寻找方法是“从左到右”的方式寻找,此时会执行B类中的say( )
执行结果:
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
say BBB
在子类中,如果想要获得父类的方法时,我们可以通过 super()来做。
super()代表父类的定义,不是父类对象。
class A:
def say(self):
print("A:",self)
print("say AAA")
class B(A):
def say(self):
# A.say(self) #调用父类的say方法
super().say() #通过super()方法调用父类的方法
print("say BBB")
b = B()
b.say()
# 执行结果:
# A: <__main__.B object at 0x0000011CB5ADBC88>
# say AAA
# say BBB
多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。在现实 生活中,我们有很多例子。比如:。同样是吃饭的方法,中国人用筷子吃饭,英国人用刀叉吃 饭,印度人用手吃饭。
关于多态要注意两点:
class Animal:
def shout(self):
print("动物叫了一声")
class Dog(Animal):
def shout(self):
print("小狗 汪汪汪")
class Cat(Animal):
def shout(self):
print("小猫 喵喵喵")
def animalShout(a):
if isinstance(a,Animal):
a.shout() #传入的对象不同 shout方法对应的实际行动也不同
animalShout(Dog())
animalShout(Cat())
执行结果:
小狗 汪汪汪
小猫 喵喵喵
Python的运算符实际上是通过调用对象的特殊方法实现的。比如:
a = 20
b = 30
c = a + b
d = a.__add__(b)
print("c=",c)
print("d=",d)
执行结果:
c= 50
d= 50
每个运算符实际上都对应了相应的方法,统计如下:
我们可以重写上面的 特殊方法实现运算符的重载:
class Person:
def __init__(self,name):
self.name = name
def __add__(self,other):
if isinstance(other,Person):
return "{0}--{1}".format(self.name,other.name)
else:
return "不是同类对象 不能相加"
def __mul__(self,other):
if isinstance(other,int):
return self.name*other
p1 = Person("shy-1")
p2 = Person("shy-2")
x = p1 + p2
print(x)
print(p1*3)
执行结果:
shy-1--shy-2
shy-1shy-1shy-1
Python对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。这 里我们列出常见的特殊属性:
class A:
pass
class B:
pass
class C(B,A):
def __init__(self,nn):
self.nn = nn
def cc(self):
print("cc")
c = C(3)
print(dir(c))
print(c.__dict__)
print(c.__class__)
print(C.__bases__)
print(C.mro())
print(A.__subclasses__)
# 执行结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'cc', 'nn']
{'nn': 3}
<class '__main__.C'>
(<class '__main__.B'>, <class '__main__.A'>)
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
<built-in method __subclasses__ of type object at 0x0000011CB5410668>
# 测试对象的引用赋值 浅拷贝 深拷贝
import copy
class MobilePhone:
def __init__(self,cpu,screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算,算个12345")
print("CPU对象",self)
class Screen:
def show(self):
print("显示一个好看的画面")
print("屏幕对象:",self)
c = CPU()
s = Screen()
m = MobilePhone(c,s)
m.cpu.calculate()
n = m #两个变量 但是指向的是同一个对象
print(m,n)
m2 = copy.copy(m) #m2是新拷贝的另一个手机对象
print(m,m2)
m.cpu.calculate()
m2.cpu.calculate() #m2和m拥有了一样的CPU对象和screen对象
m3 = copy.deepcopy(m)
m3.cpu.calculate() #m3和m拥有不一样的CPU和screen对象
#执行结果:
计算,算个12345
CPU对象 <__main__.CPU object at 0x0000011CB5ACBA90>
<__main__.MobilePhone object at 0x0000011CB5ABE2E8> <__main__.MobilePhone object at 0x0000011CB5ABE2E8>
<__main__.MobilePhone object at 0x0000011CB5ABE2E8> <__main__.MobilePhone object at 0x0000011CB5ACBCC0>
计算,算个12345
CPU对象 <__main__.CPU object at 0x0000011CB5ACBA90>
计算,算个12345
CPU对象 <__main__.CPU object at 0x0000011CB5ACBA90>
计算,算个12345
CPU对象 <__main__.CPU object at 0x0000011CB5AA2BE0>
“is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。“is-a” 关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。” has-a”关系指的是这样的关系:手机拥有 CPU。 MobilePhone has a CPU。
# 组合测试
class MobilePhone:
def __init__(self,cpu,screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算")
class Screen:
def show(self):
print("显示")
c = CPU()
s = Screen()
m = MobilePhone(c,s)
m.cpu.calculate() #通过组合我们也可以调用CPU对象的方法 相当于手机对象间接的拥有了CPU的方法
# m.Screen.show()
计算
设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进 行统一的管理和控制
# 工厂模式
class CarFactory:
def createCar(self,brand):
if brand == "奔驰":
return Benz()
elif brand == "宝马":
return BMW()
elif brand == "比亚迪":
return BYD()
else:
return "未知品牌"
class Benz:
pass
class BMW:
pass
class BYD:
pass
factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")
print(c1)
print(c2)
执行结果:
<__main__.Benz object at 0x0000011CB5A6F438>
<__main__.BMW object at 0x0000011CB5A6F358>
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一 个访问该实例的全局访问点。
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较 多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久 驻留内存中,从而极大的降低开销。
单例模式有多种实现的方式,推荐重写__new__()的方法。
# 单例模式
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls,*args,**kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self,name):
if MySingleton.__init_flag:
print("init....")
self.name = name
MySingleton.__init_flag = False
a = MySingleton("aa")
print(a)
b = MySingleton("bb")
print(b)
执行结果:
init....
<__main__.MySingleton object at 0x0000011CB5B252B0>
<__main__.MySingleton object at 0x0000011CB5B252B0>
设计模式称之为“模式”,就是一些固定的套路。我们很容易用到其他场景上,比如前面讲 的工厂模式,我们需要将工厂类定义成“单例”,只需要简单的套用即可实现:
class CarFactory:
__obj = None
__init_flag = True
def __new__(cls,*args,**kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self):
if CarFactory.__init_flag:
print("init....")
CarFactory.__init_flag = False
def createCar(self,brand):
if brand == "奔驰":
return Benz()
elif brand == "宝马":
return BMW()
elif brand == "比亚迪":
return BYD()
else:
return "未知品牌"
class Benz:
pass
class BMW:
pass
class BYD:
pass
factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")
print(c1)
print(c2)
factory2 = CarFactory()
print(factory)
print(factory2)
执行结果:
init....
<__main__.Benz object at 0x0000011CB5B76208>
<__main__.BMW object at 0x0000011CB5B76278>
<__main__.CarFactory object at 0x0000011CB5B76240>
<__main__.CarFactory object at 0x0000011CB5B76240>