零基础小白的Python学习之路(六)面向对象编程
面向对象
参考资料:《Python编程 从入门到实战》+ 尚学堂Python400集
面向对象编程
面向对象(Object oriented Programming,OOP)编程的思想主要是针对大型软件设计而来,增强程序的扩展性、可读性。
面向对象编程将数据和操作数据相关的方法封装到对象中,组织代码和数据的方式更加接近人的思维,从而大大提高编程效率
Python完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能(继承、多态、封装等)
Python中,一切皆对象。
Python支持:
- 面向过程
- 面向对象
- 函数式编程
面向对象 VS 面向过程
面向过程(Procedure Oriented)
程序的逻辑流程,执行者思维,适合编写小规模的程序
思考怎么按步骤实现,第一步该干什么,第二步该干什么,等等。但是面对大型复杂的程序时,需要多方协作,这时面向过程就不再适用了
面向对象(Object oriented)
面向对象更关注“软件中的对象之间的关系”,是一种“设计者”思维,适合编写大规模程序,这更契合人的思维模式,首先思考怎么设计这个东西,这是一种不同于面向过程的思维上的转变。
遇到复杂问题,先从问题中找名词(面向过程更多的是找动词),然后确立这些名词哪些可以作为类,再根据问题需求确定类的属性和方法,确定类之间的关系。
比如举个例子,造车。汽车内部具有非常复杂的结构,我们想简单通过面向过程去实现是很困难的,采用面向对象就好很多,首先我们会思考一部跑车由哪些元素 / 部件构成,比如发动机、轮胎、车壳、座椅、挡风玻璃,一些高性能配置还需额外的部件。
为了充分体现协作的好处,我们需要找各自负责的厂家进行各自零部件(对象)的设计与制造,同一段时间,各大厂商同时进行各自部件的制造(方法),大大提高了生产效率,但要注意的是,细化到每个零部件的设计制造,同样是一种流水线作业,依旧是面向过程的思维,但是宏观上由面向对象去把握,细化到每一个部件有具体的方法去实现,这就是面向对象的过程以及好处,同时面向对象与面向过程相辅相成,并不是独立的。
【总结】
- 两者都是解决问题的思维方式,都是代码组织的方式
- 解决简单问题可以使用面向过程
- 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程
面向对象三大特征
- 封装(隐藏)
- 继承
- 多态
封装(隐藏)
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。
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): # Student继承了Person,Person继承了object
def __init__(self,name,age,score):
self.score = score
Person.__init__(self,name,age)
print(Student.mro())
Student继承了Person,Person继承了object
【测试2】
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): # Student继承了Person,Person继承了object
def __init__(self,name,age,score):
Person.__init__(self,name,age) # 必须显式调用父类初始化方法,不然解释器不会自己去调用
self.score = score
print("分数是:",self.score)
print(Student.mro())
s = Student("Jack",24,98)
s.say_age()
print(s.name)
print(s.score)
print(s._Person__age) # 读取父类的私有属性
类成员的继承和重写
- 成员继承:子类继承了父类除构造方法之外的所有成员
- 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
【测试】
class Person:
def __init__(self,name,age):
self.name = name
self.__age = age # 私有
def say_age(self):
print(self.name,"的年龄是:",self.__age)
def say_intr(self):
print("我的名字是{0}".format(self.name)) # 这个被子类的覆盖(重写)了
class Student(Person): # Student继承了Person,Person继承了object
def __init__(self,name,age,score):
Person.__init__(self,name,age) # 必须显式调用父类初始化方法,不然解释器不会自己去调用
self.score = score
print("分数是:",self.score)
def say_intr(self):
'''重写了父类的方法'''
print("我是{0},呵呵".format(self.name))
s = Student("Kevin",24,90)
s.say_age()
s.say_intr()
查看类的继承层次结构
通过类的方法mro()或者类的属性__mro __可以输出这个类的继承层次结构
class A:
pass
class B(A):
pass
class C(B):
pass
print(C.mro())
print(C.__mro__)
object根类
object是所有类的父类,所有类都具有object类的属性和方法
dir()查看对象属性
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def say_age(self):
print(self.namne,"的年龄是:",self.age)
obj = object()
print(dir(obj))
s2 = Person("Stefan",24)
print(dir(s2))
【执行结果】
[‘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’]
【注】
- Person对象增加了6个属性:
__dict __,__module __,__weakref __,age, name , say_age - Person类作为object的子类,显然包含了所有的属性
- 打印age,name,say_age,发现say_age虽然是方法,实际上也是属性。只不过,这个属性的类型是“method”而已
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))
s2 = Person("Stefan",24)
print(dir(s2))
print(type(s2.age))
print(type(s2.name))
print(type(s2.say_age))
重写__str__方法
object有一个__str __()方法,用于返回一个对于“对象的描述”,对应于内置函数str();经常用于print()方法,帮助我们查看对象的信息;__str __()可以重写
【原来】
class Person: # 默认继承object
def __init__(self,name,age):
self.name = name
self.age = age
p = Person("Jim",24)
print(p)
【重写后】
class Person: # 默认继承object
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self): # 重写
return "名字是:{0}".format(self.name)
p = Person("Jim",24)
print(p)
多重继承
Python支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。但是这样会被“类的整体层次”搞得异常复杂,尽量避免使用。
class A:
def aa(self):
print("aa")
class B:
def bb(self):
print("bb")
class C(B,A):
def cc(self):
print("cc")
c = C()
c.cc()
c.bb()
c.aa()
MRO()
Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将“从左向右”按顺序搜索
MRO(Method Resolution Order):方法解析顺序。
我们可以通过mro()方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
super()获得父类定义
在子类中,如果想要获得父类的方法时,我们可以通过super()来做。
super()代表父类的定义(代码),不是父类对象
# 测试super() 代表父类定义
class A:
def say(self):
print("A:",self)
class B(A):
def say(self):
# A.say(self)
super().say()
print("B:",self)
B().say()
【多态】
多态(polymorphism),指同一个方法调用由于对象不同可能会产生不同的行为。
【注】
- 多态是方法的多态,属性没有多态
- 多态的存在有2个必要条件:继承 和 方法重写
【测试】
class Animal:
def shout(self):
print("动物叫。。。")
class Dog(Animal):
def shout(self):
print("汪汪汪")
class Cat(Animal):
def shout(self):
print("喵喵喵")
class Sheep(Animal):
def shout(self):
print("咩咩咩")
def animal_shout(m):
if isinstance(m,Animal):
m.shout() # 方法多态,一个方法调用,根据不同的对象调用不同的方法
else:
print("它不叫")
animal_shout(Animal())
animal_shout(Dog())
animal_shout(Cat())
animal_shout(Sheep())
对象的进化
随着编程面临的问题越来越复杂,编程语言本身也在进化。从主要处理简单数据开始,随着数据变多进化为“数组”;数据类型变复杂,进化出了“结构体”;处理数据的方式和逻辑变复杂,进化出了“对象”。
类的定义
通过类定义数据类型的属性(数据)和方法(行为),也就是说,“类将行为和状态打包在一起”
对象是类的具体实体,一般称为“类的实例”,就好比一个糕点模具,对象根据这个模具制造出各种甜点。
从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点,“方法代码是共享的,属性数据不共享。”
定义类的语法格式:
class 类名:
类体(就是一些属性和方法等)
【注】
- 类名必须符合“标识符”规则(一般,首字母大写,多个单词—>驼峰原则)
- 类体中我们可以定义属性和方法
- 属性用来描述数据,方法(即函数)用来描述这些数据相关的操作
【例】
class Student:
def __init__(self,name,score): # 构造函数 self必须位于第一个参数 self对象本身
self.name = name
self.score = score
def say_score(self): # self必须位于第一个参数
print("{0}的分数是:{1}".format(self.name,self.score))
s1 = Student("Stefan",96) # 调用构造方法(构造器)初始化一个对象
s1.say_score()
构造函数__init__()
类是抽象的,也称为“对象的模板”。我们需要通过类这个模板,创建类的实例对象,然后才能使用类定义的功能
进一步展开Python对象包含的三个部分:
- id(identity 识别码)
- type(对象类型)
- value(对象的值)
-属性(attribute)
-方法(method)
创建对象,我们需要定义构造函数__init__()方法
构造方法用于执行“实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值
【注】
- 名称固定:__ init __
- 第一个参数固定,必须为:self(self:刚刚创建好的实例对象)
- 构造函数通常用来初始化实例对象的实例属性
如
def __init__(self,name,score): # 实例属性
self.name = name
self.score = score
- 通过“类名(参数列表)”来调用构造函数。调用后,将创建好的对象返回给相应的变量
- __ init __()方法:初始化创建好的对象(初始化:给实例属性赋值)
- __ new __()方法:用于创建对象,但我们一般无需重定义该方法
【注】
Python中的self相当于 C++ 中的self指针,JAVA 和 C# 中的this关键字。Python中,self必须为构造函数的第一个参数,名字可以任意修改,但一般遵守惯例,都叫做self。
实例属性
实例属性是从属于实例对象的属性,也称为“实例变量”。
几个要点:
- 实例属性一般在__ init __()方法中通过如下代码定义:
self.实例属性名 = 初始值 - 在本类的其他实例方法中,也是通过self进行访问:
self.实例属性名 - 创建实例对象后,通过实例对象访问:
obj01 = 类名() # 创建对象,调用__ init __()初始化属性
obj01.实例属性名 = 值 # 可以给已有属性赋值,也可以新加属性
class Student:
def __init__(self,name,score): # self必须位于第一个参数 self对象本身,
self.name = name
self.score = score
def say_score(self): # self必须位于第一个参数
print("{0}的分数是:{1}".format(self.name,self.score))
s1 = Student("Stefan",96) # 调用构造方法初始化一个对象
s1.say_score()
s1.age = 24
s1.salary = 30000
print(s1.salary)
s2 = Student("XXC",23)
print(s2.name)
【内存分析】
实例方法
实例方法是从属于实例对象的方法
基本格式:
def 方法名(self [, 形参列表])
函数体
方法的调用格式如下:
对象.方法名([实参列表])
【注】
- 定义实例方法时,第一个参数必须为self。和前面一样,self指当前的实例对象
- 调用实例方法时,不需要也不能给self传参。self由解释器自动传参。
【注】区别函数和方法
- 都是用来完成一个功能的语句块,本质是一样的。
- 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
- 直观上看,方法定义时需要传递self,函数不需要。
【例】
s2 = Student("XXC",77)
s2.say_score()
Student.say_score(s2) # 解释器实际的调用 和上句输出结果是一样的
【内存分析】
【其他操作】:
- dir(obj):可以获得对象的所有属性、方法
- obj.__ dict __:对象的属性字典
- pass:空语句
- isinstance(对象 , 类型):判断“对象”是不是“指定类型”
print(dir(s2)) # 所有的
print(s2.__dict__) # 只是我们定义的
class Man:
pass
print(isinstance(s2,Student))
类对象
当解释器执行class语句时,就会创建一个类对象
【测试类对象】
class Student:
pass
print(type(Student))
print(id(Student))
Stu2 = Student
s1 = Stu2()
print(s1)
类属性和类方法
类属性
从属于“类对象”的属性,也称为“类变量”。由于类属性从属于类对象,可以被所有实例对象共享
类属性定义方式:
class 类名:
类变量名 = 初始值
在类中或者类的外面,我们可以通过:“类名.类变量名”来读写
class Student:
company = "STC" # 类属性
count = 0 # 类属性
def __init__(self,name,score):
self.name = name # 实例属性
self.score = score
Student.count = Student.count + 1
def say_score(self): # 实例方法
print("My Company is:",Student.company)
print(self.name,'的分数是:',self.score)
s1 = Student('Stefan',84) # s1是实例对象,自动调用__init__()方法
s1.say_score()
print(id(s1))
s2 = Student('Jack',90)
s3 = Student('Jim',89)
print('一共创建{0}个Student对象'.format(Student.count))
【内存分析】
类方法
从属于“类对象”的方法。类方法通过装饰器@classmethod来定义,格式如下:
@classmethod
def 类方法名(cls [, 形参列表]):
函数体
【注】
- @classmethod 必须位于方法上面第一行
- 第一个cls必须有;cls(指“类对象”本身)
- 调用类方法格式:“类名.类方法名(参数列表)”。参数列表中,不需要也不能给cls传值
- 类方法中访问实例属性和实例方法会导致错误
- 子类继承父类方法时,传入cls是子类对象,而非父类对象
【测试】
class Student:
company = "STC"
@classmethod # 只属于类
def printCompany(cls):
print(cls.company)
Student.printCompany()
静态方法
Python中允许定义与“类对象”无关的方法,称为“静态方法”
“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空间里面”,需要通过“类调用”。
静态方法通过装饰器@staticmethod来定义,格式:
@staticmethod
def 静态方法名([形参列表]):
函数体
【注】
- @staticmethod 必须位于方法上面一行
- 调用静态方法格式:“类名.静态方法名(参数列表)”
- 静态方法中访问实例属性和实例方法会导致错误(调不了self)
class Student2:
company = "STC"
@staticmethod
def add(a,b): # 静态方法
print("{0} + {1} = {2}".format(a,b,(a+b)))
return a+b
Student2.add(20,30)
__ del __方法(析构函数)和垃圾回收机制
__ del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。
Python实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收器调用__ del __方法
我们也可以通过del语句删除对象,从而保证调用__del __方法
系统会自动提供__del __方法,一般不需要自定义析构方法
【垃圾回收机制】
【测试】
# 析构函数
class Person:
def __del__(self):
print("销毁对象{0}".format(self))
p1 = Person()
p2 = Person()
del p2
print("程序结束")
print(p1)
print(p2)
【注】p2已经被销毁了,所以会报错
__call __方法和可调用对象
定义了__call __方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用
【测试】
# 测试可调用方法__call__()
class SalaryAccount:
'''工资计算'''
def __call__(self, salary):
print('算工资')
yearSalary = salary * 12
# monthsalary = salary
daySalary = salary // 22.5
hourSalary = daySalary // 8
return dict(yearSalary = yearSalary,monthSalary = salary,daySalary = daySalary,hourSalary = hourSalary)
s = SalaryAccount()
print(s(30000))
Python中理解的“方法重载”
在C++等其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。
方法签名包含3个部分:方法名、参数数量、参数类型
Python中,方法的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,Python中是没有方法的重载的。
定义一个方法即可有多种调用方式,相当于实现了其他语言中的方法的重载
注意,如果我们在类体中定义了多个重名的方法,只有最后一个方法有效!
总结起来,Python是没有重载的
方法的动态性
Python是动态语言,我们可以动态地为类添加新的方法,或者动态地修改类已有的方法
# 测试动态性
class Person:
def work(self):
print("Hard Work!")
def play_game(s):
print("{0} is playing game!".format(s))
def work2(s):
print("Study hard")
Person.play = play_game
p = Person()
p.work()
p.play() # Person.play(p)
Person.work = work2
p.work()
私有属性和私有方法(实现封装)
Python中对类的成员并没有严格的访问控制限制。
【注】
- 通常我们约定,两个下划线开头的属性是私有的(private),其他为公共的(public)。
- 类内部可以访问私有属性(方法)。
- 内外部不能直接访问私有属性(方法)。
- 内外部可以通过“_ 类名__私有属性(方法)名”(注意是前面一个下划线,后面两个下划线)访问私有属性(方法)。
【注】方法本质上也是属性,只不过是可以通过()执行而已。所以,此处讲的私有属性和公有属性,也同时讲解了私有方法和公有方法的用法。
【测试1】
# 测试私有属性
class Employee:
def __init__(self,name,age):
self.name = name # 公有
self.__age = age # 私有
e = Employee("Stefan",24)
print(e.name)
print(e._Employee__age)
print(dir(e))
【测试2】
# 测试私有方法
class Employee:
__company = "BTS"
def __init__(self,name,age):
self.name = name # 公有
self.__age = age # 私有
def __work(self): # 私有方法
print("Work hard makes perfect")
print("年龄:{0}".format(self.__age))
print(Employee.__company)
e = Employee("Stefan",24)
print(e.name)
print(e._Employee__age)
e._Employee__work()
print(e._Employee__company)
print(Employee._Employee__company)
print(dir(e))
@property装饰器
@property 可以将一个方法的调用方式变成“属性调用”。
# 测试@property(属性)
class Employee:
@property
def salary(self):
print("salary is running...")
return 10000
emp1 = Employee()
# emp1.salary()
print(emp1.salary) # 属性调用
emp1.salary = 20000 # 允许调用,但不允许设置,会报错
【测试1】不用装饰器自己定义get、set方法
class Employee:
def __init__(self,name,salary):
self.__name = name
self.__salary = salary
def get_salary(self):
return self.__salary
def set_salary(self,salary):
if 1000 < salary < 50000:
self.__salary = salary
else:
print("录入错误!薪水在1000~50000范围内")
emp1 = Employee("Jack",30000)
print(emp1.get_salary())
emp1.set_salary(-2000)
print(emp1.get_salary())
【测试2】采用@property 装饰器
# 测试 @property(属性)
class Employee:
def __init__(self,name,salary):
self.__name = name
self.__salary = salary
@property
def salary(self):
return self.__salary
@property
def name(self):
return self.__name
@salary.setter
def salary(self,salary):
if 1000 < salary < 50000:
self.__salary = salary
else:
print("录入错误!薪水在1000~50000范围内")
@name.setter
def name(self,name):
if len(name) > 4:
print("录入错误!姓名越界")
else:
self.__name = name
emp1 = Employee("Jack",30000)
print(emp1.name,emp1.salary)
emp1.name = "KKJHGGGJH"
emp1.salary = 2990
print(emp1.name,emp1.salary)
特殊方法和运算符重载
Python的运算符实际上是通过调用对象的特殊方法实现的
比如:
a = 20
b = 30
c = a + b
d = a.__add__(b)
print("c = ",c)
print("d = ",d)
【常见特殊方法】
【运算符对应方法】
【测试】
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)
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
else:
return "不是同类对象,不能相乘!"
p1 = Person("Stefan",24)
p2 = Person("Jack",18)
x = p1 + p2 # __add__重载
print(x)
print(p1.name * 3) # __mul__重载
print(p2._Person__age * 3)
print(p1) # print重载
print(p2)
特殊属性
Python对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。
【常见的特殊属性】
【测试】
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 __init__(self,name,age):
self.name = name
self.age = age
def cc(self):
print("cc")
c = C("Kim",45)
print(C.mro()) # 类层次结构
c.say()
print(dir(c))
print(c.__dict__) # 对象的属性字典
print(c.__class__) # 对象所属的类
print(C.__bases__) # 类的基类元组(多继承)
print(C.__base__) # 类的基类
print(A.__subclasses__()) # 子类列表
print(C.__mro__) # 类层次结构
对象的浅拷贝和深拷贝
【测试】
import copy
class Phone:
def __init__(self,cpu,screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算")
print("cpu对象",self)
class Screen:
def show(self):
print("显示出来!")
print("screen对象",self)
# 测试变量赋值
print("测试变量赋值")
c1 = CPU()
c2 = c1
print(c1)
print(c2)
# 测试浅拷贝
print("测试浅拷贝")
s1 = Screen()
m1 = Phone(c1,s1)
m2 = copy.copy(m1)
print(m1,m1.cpu,m1.screen)
print(m2,m2.cpu,m2.screen)
# 测试深拷贝
print("测试深拷贝")
m3 = copy.deepcopy(m1)
print(m1,m1.cpu,m1.screen)
print(m3,m3.cpu,m3.screen)
组合
“is-a”关系,我们可以使用“继承”,从而实现子类拥有的父类的方法和属性。(前者 is a 后者–前者继承后者类)
“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。(前者 has a 后者)
【测试1】
# 继承
class A1:
def say_a1(self):
print("a1,a1,a1")
class B1(A1):
pass
b1 = B1()
b1.say_a1()
# 组合
class A2:
def say_a2(self):
print("a2,a2,a2")
class B2:
def __init__(self,a):
self.a = a
a2 = A2()
b2 = B2(a2)
b2.a.say_a2()
# b2拥有a2
【测试2】
class Phone:
def __init__(self,cpu,screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print("计算")
print("cpu对象",self)
class Screen:
def show(self):
print("显示出来!")
print("screen对象",self)
m = Phone(CPU(),Screen())
m.cpu.calculate()
m.screen.show()
设计模式
设计模式是面向对象语言特有的内容
比较流行的有:GOF23种设计模式
GOF(Goup Of Four)
工厂模式
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类,创建对象进行统一的管理和控制。
【测试】
# 测试工厂模式
class CarFactory:
def create_car(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.create_car("奔驰")
c2 = factory.create_car("比亚迪")
print(c1)
print(c2)
单例模式
单例模式(Singleton Pattern)核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如,读取配置文件,产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销
【测试1】
# 测试单例模式
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")
b = MySingleton("bb")
c = MySingleton("cc")
print(a)
print(b)
print(c)
本文地址:https://blog.csdn.net/qq_35495464/article/details/106982131
上一篇: 实用openpyxl的基本读写操作
下一篇: 基于ROS系统的手部追踪器程序设计